summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/tests
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/utils/tests')
-rw-r--r--devtools/client/debugger/src/utils/tests/DevToolsUtils.spec.js41
-rw-r--r--devtools/client/debugger/src/utils/tests/__snapshots__/ast.spec.js.snap53
-rw-r--r--devtools/client/debugger/src/utils/tests/__snapshots__/expressions.spec.js.snap25
-rw-r--r--devtools/client/debugger/src/utils/tests/__snapshots__/function.spec.js.snap25
-rw-r--r--devtools/client/debugger/src/utils/tests/__snapshots__/indentation.spec.js.snap27
-rw-r--r--devtools/client/debugger/src/utils/tests/assert.spec.js30
-rw-r--r--devtools/client/debugger/src/utils/tests/ast.spec.js34
-rw-r--r--devtools/client/debugger/src/utils/tests/build-query.spec.js256
-rw-r--r--devtools/client/debugger/src/utils/tests/clipboard.spec.js45
-rw-r--r--devtools/client/debugger/src/utils/tests/expressions.spec.js67
-rw-r--r--devtools/client/debugger/src/utils/tests/function.spec.js61
-rw-r--r--devtools/client/debugger/src/utils/tests/indentation.spec.js61
-rw-r--r--devtools/client/debugger/src/utils/tests/isMinified.spec.js18
-rw-r--r--devtools/client/debugger/src/utils/tests/location.spec.js31
-rw-r--r--devtools/client/debugger/src/utils/tests/log.spec.js35
-rw-r--r--devtools/client/debugger/src/utils/tests/memoize.spec.js48
-rw-r--r--devtools/client/debugger/src/utils/tests/memoizeLast.spec.js31
-rw-r--r--devtools/client/debugger/src/utils/tests/path.spec.js49
-rw-r--r--devtools/client/debugger/src/utils/tests/quick-open.spec.js35
-rw-r--r--devtools/client/debugger/src/utils/tests/result-list.spec.js32
-rw-r--r--devtools/client/debugger/src/utils/tests/source.spec.js367
-rw-r--r--devtools/client/debugger/src/utils/tests/telemetry.spec.js13
-rw-r--r--devtools/client/debugger/src/utils/tests/text.spec.js20
-rw-r--r--devtools/client/debugger/src/utils/tests/ui.spec.js15
-rw-r--r--devtools/client/debugger/src/utils/tests/url.spec.js89
-rw-r--r--devtools/client/debugger/src/utils/tests/utils.spec.js87
-rw-r--r--devtools/client/debugger/src/utils/tests/wasm.spec.js96
27 files changed, 1691 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/tests/DevToolsUtils.spec.js b/devtools/client/debugger/src/utils/tests/DevToolsUtils.spec.js
new file mode 100644
index 0000000000..be405d8628
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/DevToolsUtils.spec.js
@@ -0,0 +1,41 @@
+/* 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/>. */
+
+import { reportException, executeSoon } from "../DevToolsUtils.js";
+
+describe("DevToolsUtils", () => {
+ describe("reportException", () => {
+ beforeEach(() => {
+ global.console = { error: jest.fn() };
+ });
+
+ it("calls console.error", () => {
+ reportException("caller", ["you broke it"]);
+ expect(console.error).toHaveBeenCalled();
+ });
+
+ it("returns a description of caller and exception text", () => {
+ const who = "who",
+ exception = "this is an error",
+ msgTxt = " threw an exception: ";
+
+ reportException(who, [exception]);
+
+ expect(console.error).toHaveBeenCalledWith(`${who}${msgTxt}`, [
+ exception,
+ ]);
+ });
+ });
+
+ describe("executeSoon", () => {
+ it("calls setTimeout with 0 ms", () => {
+ global.setTimeout = jest.fn();
+ const fnc = () => {};
+
+ executeSoon(fnc);
+
+ expect(setTimeout).toHaveBeenCalledWith(fnc, 0);
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/__snapshots__/ast.spec.js.snap b/devtools/client/debugger/src/utils/tests/__snapshots__/ast.spec.js.snap
new file mode 100644
index 0000000000..7fe8b4f716
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/__snapshots__/ast.spec.js.snap
@@ -0,0 +1,53 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`find the best expression for the token should find the expression for the property 1`] = `
+Object {
+ "computed": false,
+ "expression": "obj.b",
+ "location": Object {
+ "end": Position {
+ "column": 17,
+ "line": 6,
+ },
+ "start": Position {
+ "column": 16,
+ "line": 6,
+ },
+ },
+ "name": "b",
+}
+`;
+
+exports[`find the best expression for the token should find the identifier 1`] = `
+Object {
+ "expression": "key",
+ "location": Object {
+ "end": Position {
+ "column": 13,
+ "line": 1,
+ },
+ "start": Position {
+ "column": 10,
+ "line": 1,
+ },
+ },
+ "name": "key",
+}
+`;
+
+exports[`find the best expression for the token should find the identifier for computed member expressions 1`] = `
+Object {
+ "expression": "key",
+ "location": Object {
+ "end": Position {
+ "column": 9,
+ "line": 5,
+ },
+ "start": Position {
+ "column": 6,
+ "line": 5,
+ },
+ },
+ "name": "key",
+}
+`;
diff --git a/devtools/client/debugger/src/utils/tests/__snapshots__/expressions.spec.js.snap b/devtools/client/debugger/src/utils/tests/__snapshots__/expressions.spec.js.snap
new file mode 100644
index 0000000000..a0d6b17bf6
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/__snapshots__/expressions.spec.js.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`expressions wrap expression should wrap an expression 1`] = `
+"try {
+ foo
+} catch (e) {
+ e
+}"
+`;
+
+exports[`expressions wrap expression should wrap expression with a comment 1`] = `
+"try {
+ foo // yo yo
+} catch (e) {
+ e
+}"
+`;
+
+exports[`expressions wrap expression should wrap quotes 1`] = `
+"try {
+ \\"2\\"
+} catch (e) {
+ e
+}"
+`;
diff --git a/devtools/client/debugger/src/utils/tests/__snapshots__/function.spec.js.snap b/devtools/client/debugger/src/utils/tests/__snapshots__/function.spec.js.snap
new file mode 100644
index 0000000000..8d83a48d3b
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/__snapshots__/function.spec.js.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`function findFunctionText finds class function 1`] = `
+"bar() {
+ 2 + 2;
+}"
+`;
+
+exports[`function findFunctionText finds function 1`] = `
+"async function exSlowFoo() {
+ return \\"yay in a bit\\";
+}"
+`;
+
+exports[`function findFunctionText finds function signature 1`] = `
+"async function exSlowFoo() {
+ return \\"yay in a bit\\";
+}"
+`;
+
+exports[`function findFunctionText finds property function 1`] = `
+"function name() {
+ 2 + 2;
+}"
+`;
diff --git a/devtools/client/debugger/src/utils/tests/__snapshots__/indentation.spec.js.snap b/devtools/client/debugger/src/utils/tests/__snapshots__/indentation.spec.js.snap
new file mode 100644
index 0000000000..883d48a7ff
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/__snapshots__/indentation.spec.js.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`indentation mad indentation 1`] = `
+"try {
+console.log(\\"yo\\")
+} catch (e) {
+console.log(\\"yo\\")
+ }"
+`;
+
+exports[`indentation one function 1`] = `
+"function foo() {
+ console.log(\\"yo\\")
+}"
+`;
+
+exports[`indentation one line 1`] = `"foo"`;
+
+exports[`indentation simple 1`] = `"foo"`;
+
+exports[`indentation try catch 1`] = `
+"try {
+ console.log(\\"yo\\")
+} catch (e) {
+ console.log(\\"yo\\")
+}"
+`;
diff --git a/devtools/client/debugger/src/utils/tests/assert.spec.js b/devtools/client/debugger/src/utils/tests/assert.spec.js
new file mode 100644
index 0000000000..031665c5d3
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/assert.spec.js
@@ -0,0 +1,30 @@
+/* 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/>. */
+
+import assert from "../assert.js";
+
+let testAssertMessageHead, testAssertMessage;
+
+describe("assert", () => {
+ beforeEach(() => {
+ testAssertMessageHead = "Assertion failure: ";
+ testAssertMessage = "Test assert.js Message";
+ });
+
+ describe("when condition is truthy", () => {
+ it("does not throw an Error", () => {
+ expect(() => {
+ assert(true, testAssertMessage);
+ }).not.toThrow();
+ });
+ });
+
+ describe("when condition is falsy", () => {
+ it("throws an Error displaying the passed message", () => {
+ expect(() => {
+ assert(false, testAssertMessage);
+ }).toThrow(new Error(testAssertMessageHead + testAssertMessage));
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/ast.spec.js b/devtools/client/debugger/src/utils/tests/ast.spec.js
new file mode 100644
index 0000000000..55e9e429d5
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/ast.spec.js
@@ -0,0 +1,34 @@
+/* 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/>. */
+
+import { findBestMatchExpression } from "../ast";
+
+import { getSymbols } from "../../workers/parser/getSymbols";
+import { populateSource } from "../../workers/parser/tests/helpers";
+
+describe("find the best expression for the token", () => {
+ const source = populateSource("computed-props");
+ const symbols = getSymbols(source.id);
+
+ it("should find the identifier", () => {
+ const expression = findBestMatchExpression(symbols, {
+ line: 1,
+ column: 13,
+ });
+ expect(expression).toMatchSnapshot();
+ });
+
+ it("should find the expression for the property", () => {
+ const expression = findBestMatchExpression(symbols, {
+ line: 6,
+ column: 16,
+ });
+ expect(expression).toMatchSnapshot();
+ });
+
+ it("should find the identifier for computed member expressions", () => {
+ const expression = findBestMatchExpression(symbols, { line: 5, column: 6 });
+ expect(expression).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/build-query.spec.js b/devtools/client/debugger/src/utils/tests/build-query.spec.js
new file mode 100644
index 0000000000..dbd0eba4a4
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/build-query.spec.js
@@ -0,0 +1,256 @@
+/* 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/>. */
+
+import buildQuery from "../build-query";
+
+describe("build-query", () => {
+ it("case-sensitive, whole-word, regex search", () => {
+ const query = buildQuery(
+ "hi.*",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: true,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("\\bhi.*\\b");
+ expect(query.flags).toBe("");
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("case-sensitive, whole-word, regex search, global", () => {
+ const query = buildQuery(
+ "hi.*",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: true,
+ },
+ { isGlobal: true }
+ );
+
+ expect(query.source).toBe("\\bhi.*\\b");
+ expect(query.flags).toBe("g");
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("case-insensitive, non-whole, string search", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: false,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("hi");
+ expect(query.flags).toBe("i");
+ expect(query.ignoreCase).toBe(true);
+ });
+
+ it("case-insensitive, non-whole, string search, global", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: false,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ { isGlobal: true }
+ );
+
+ expect(query.source).toBe("hi");
+ expect(query.flags).toBe("gi");
+ expect(query.ignoreCase).toBe(true);
+ });
+
+ it("case-sensitive string search", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("hi");
+ expect(query.flags).toBe("");
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("string search with wholeWord", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: false,
+ wholeWord: true,
+ regexMatch: false,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("\\bhi\\b");
+ expect(query.flags).toBe("i");
+ expect(query.ignoreCase).toBe(true);
+ });
+
+ it("case-insensitive, regex search", () => {
+ const query = buildQuery(
+ "hi.*",
+ {
+ caseSensitive: false,
+ wholeWord: false,
+ regexMatch: true,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("hi.*");
+ expect(query.flags).toBe("i");
+ expect(query.global).toBe(false);
+ expect(query.ignoreCase).toBe(true);
+ });
+
+ it("string search with wholeWord and case sensitivity", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: false,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("\\bhi\\b");
+ expect(query.flags).toBe("");
+ expect(query.global).toBe(false);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("string search with wholeWord and case sensitivity, global", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: false,
+ },
+ { isGlobal: true }
+ );
+
+ expect(query.source).toBe("\\bhi\\b");
+ expect(query.flags).toBe("g");
+ expect(query.global).toBe(true);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("string search with regex chars escaped", () => {
+ const query = buildQuery(
+ "hi.*",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: false,
+ },
+ {}
+ );
+
+ expect(query.source).toBe("\\bhi\\.\\*\\b");
+ expect(query.flags).toBe("");
+ expect(query.global).toBe(false);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("string search with regex chars escaped, global", () => {
+ const query = buildQuery(
+ "hi.*",
+ {
+ caseSensitive: true,
+ wholeWord: true,
+ regexMatch: false,
+ },
+ { isGlobal: true }
+ );
+
+ expect(query.source).toBe("\\bhi\\.\\*\\b");
+ expect(query.flags).toBe("g");
+ expect(query.global).toBe(true);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("ignore spaces w/o spaces", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ { ignoreSpaces: true }
+ );
+
+ expect(query.source).toBe("hi");
+ expect(query.flags).toBe("");
+ expect(query.global).toBe(false);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("ignore spaces w/o spaces, global", () => {
+ const query = buildQuery(
+ "hi",
+ {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ { isGlobal: true, ignoreSpaces: true }
+ );
+
+ expect(query.source).toBe("hi");
+ expect(query.flags).toBe("g");
+ expect(query.global).toBe(true);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("ignore spaces w/ spaces", () => {
+ const query = buildQuery(
+ " ",
+ {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ { ignoreSpaces: true }
+ );
+
+ expect(query.source).toBe("(?!\\s*.*)");
+ expect(query.flags).toBe("");
+ expect(query.global).toBe(false);
+ expect(query.ignoreCase).toBe(false);
+ });
+
+ it("ignore spaces w/ spaces, global", () => {
+ const query = buildQuery(
+ " ",
+ {
+ caseSensitive: true,
+ wholeWord: false,
+ regexMatch: false,
+ },
+ { isGlobal: true, ignoreSpaces: true }
+ );
+
+ expect(query.source).toBe("(?!\\s*.*)");
+ expect(query.flags).toBe("g");
+ expect(query.global).toBe(true);
+ expect(query.ignoreCase).toBe(false);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/clipboard.spec.js b/devtools/client/debugger/src/utils/tests/clipboard.spec.js
new file mode 100644
index 0000000000..dcba730149
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/clipboard.spec.js
@@ -0,0 +1,45 @@
+/* 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/>. */
+
+import { copyToTheClipboard } from "../clipboard";
+
+let clipboardTestCopyString, expectedCopyEvent;
+const addEventListener = jest.fn();
+const execCommand = jest.fn();
+const removeEventListener = jest.fn();
+
+describe("copyToTheClipboard()", () => {
+ beforeEach(() => {
+ expectedCopyEvent = "copy";
+ clipboardTestCopyString = "content intended for clipboard";
+
+ global.document.addEventListener = addEventListener;
+ global.document.execCommand = execCommand;
+ global.document.removeEventListener = removeEventListener;
+ });
+
+ it("listens for 'copy' event", () => {
+ copyToTheClipboard(clipboardTestCopyString);
+
+ expect(document.addEventListener).toHaveBeenCalledWith(
+ expectedCopyEvent,
+ expect.anything()
+ );
+ });
+
+ it("calls document.execCommand() with 'copy' command", () => {
+ copyToTheClipboard(clipboardTestCopyString);
+
+ expect(execCommand).toHaveBeenCalledWith(expectedCopyEvent, false, null);
+ });
+
+ it("removes event listener for 'copy' event", () => {
+ copyToTheClipboard(clipboardTestCopyString);
+
+ expect(document.removeEventListener).toHaveBeenCalledWith(
+ expectedCopyEvent,
+ expect.anything()
+ );
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/expressions.spec.js b/devtools/client/debugger/src/utils/tests/expressions.spec.js
new file mode 100644
index 0000000000..b7c91c4e07
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/expressions.spec.js
@@ -0,0 +1,67 @@
+/* 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/>. */
+
+import {
+ wrapExpression,
+ getExpressionResultGripAndFront,
+} from "../expressions";
+import { makeMockExpression } from "../test-mockup";
+
+function createError(type, preview) {
+ return makeMockExpression({
+ result: { getGrip: () => ({ class: type, isError: true, preview }) },
+ });
+}
+
+describe("expressions", () => {
+ describe("wrap expression", () => {
+ it("should wrap an expression", () => {
+ expect(wrapExpression("foo")).toMatchSnapshot();
+ });
+
+ it("should wrap expression with a comment", () => {
+ expect(wrapExpression("foo // yo yo")).toMatchSnapshot();
+ });
+
+ it("should wrap quotes", () => {
+ expect(wrapExpression('"2"')).toMatchSnapshot();
+ });
+ });
+
+ describe("sanitize input", () => {
+ it("sanitizes quotes", () => {
+ expect('foo"').toEqual('foo"');
+ });
+
+ it("sanitizes 2 quotes", () => {
+ expect('"3"').toEqual('"3"');
+ });
+
+ it("evaluates \\u{61} as a", () => {
+ expect("\u{61}").toEqual("a");
+ });
+
+ it("evaluates N\\u{61}N as NaN", () => {
+ expect("N\u{61}N").toEqual("NaN");
+ });
+ });
+
+ describe("getValue", () => {
+ it("Reference Errors should be shown as (unavailable)", () => {
+ const { expressionResultGrip } = getExpressionResultGripAndFront(
+ createError("ReferenceError", { name: "ReferenceError" })
+ );
+ expect(expressionResultGrip).toEqual({
+ unavailable: true,
+ });
+ });
+
+ it("Errors messages should be shown", () => {
+ const { expressionResultGrip } = getExpressionResultGripAndFront(
+ createError("Error", { name: "Foo", message: "YO" })
+ );
+ expect(expressionResultGrip).toEqual("Foo: YO");
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/function.spec.js b/devtools/client/debugger/src/utils/tests/function.spec.js
new file mode 100644
index 0000000000..7376aa1280
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/function.spec.js
@@ -0,0 +1,61 @@
+/* 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/>. */
+
+import { findFunctionText } from "../function";
+
+import { getSymbols } from "../../workers/parser/getSymbols";
+import { populateOriginalSource } from "../../workers/parser/tests/helpers";
+
+describe("function", () => {
+ describe("findFunctionText", () => {
+ it("finds function", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+ const text = findFunctionText(14, source, source.content, symbols);
+ expect(text).toMatchSnapshot();
+ });
+
+ it("finds function signature", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+
+ const text = findFunctionText(13, source, source.content, symbols);
+ expect(text).toMatchSnapshot();
+ });
+
+ it("misses function closing brace", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+
+ const text = findFunctionText(15, source, source.content, symbols);
+
+ // TODO: we should try and match the closing bracket.
+ expect(text).toEqual(null);
+ });
+
+ it("finds property function", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+
+ const text = findFunctionText(29, source, source.content, symbols);
+ expect(text).toMatchSnapshot();
+ });
+
+ it("finds class function", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+
+ const text = findFunctionText(33, source, source.content, symbols);
+ expect(text).toMatchSnapshot();
+ });
+
+ it("cant find function", () => {
+ const source = populateOriginalSource("func");
+ const symbols = getSymbols(source.id);
+
+ const text = findFunctionText(20, source, source.content, symbols);
+ expect(text).toEqual(null);
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/indentation.spec.js b/devtools/client/debugger/src/utils/tests/indentation.spec.js
new file mode 100644
index 0000000000..5ee7419371
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/indentation.spec.js
@@ -0,0 +1,61 @@
+/* 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/>. */
+
+import { correctIndentation, getIndentation } from "../indentation";
+
+describe("indentation", () => {
+ it("simple", () => {
+ expect(
+ correctIndentation(`
+ foo
+ `)
+ ).toMatchSnapshot();
+ });
+
+ it("one line", () => {
+ expect(correctIndentation("foo")).toMatchSnapshot();
+ });
+
+ it("one function", () => {
+ const text = `
+ function foo() {
+ console.log("yo")
+ }
+ `;
+
+ expect(correctIndentation(text)).toMatchSnapshot();
+ });
+
+ it("try catch", () => {
+ const text = `
+ try {
+ console.log("yo")
+ } catch (e) {
+ console.log("yo")
+ }
+ `;
+
+ expect(correctIndentation(text)).toMatchSnapshot();
+ });
+
+ it("mad indentation", () => {
+ const text = `
+ try {
+ console.log("yo")
+ } catch (e) {
+ console.log("yo")
+ }
+ `;
+
+ expect(correctIndentation(text)).toMatchSnapshot();
+ });
+});
+
+describe("indentation length", () => {
+ it("leading spaces", () => {
+ const line = " console.log('Hello World');";
+
+ expect(getIndentation(line)).toEqual(16);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/isMinified.spec.js b/devtools/client/debugger/src/utils/tests/isMinified.spec.js
new file mode 100644
index 0000000000..1c49f96737
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/isMinified.spec.js
@@ -0,0 +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/>. */
+
+import { isMinified } from "../isMinified";
+import { makeMockSourceWithContent } from "../test-mockup";
+
+describe("isMinified", () => {
+ it("no indents", () => {
+ const sourceWithContent = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ undefined,
+ "function base(boo) {\n}"
+ );
+ expect(isMinified(sourceWithContent, sourceWithContent.content)).toBe(true);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/location.spec.js b/devtools/client/debugger/src/utils/tests/location.spec.js
new file mode 100644
index 0000000000..2be71f3cae
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/location.spec.js
@@ -0,0 +1,31 @@
+/* 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/>. */
+
+import { sortSelectedLocations } from "../location";
+
+function loc(line, column) {
+ return {
+ location: { sourceId: "foo", line, column },
+ generatedLocation: { sourceId: "foo", line, column },
+ };
+}
+describe("location.spec.js", () => {
+ it("sorts lines", () => {
+ const a = loc(3, 5);
+ const b = loc(1, 10);
+ expect(sortSelectedLocations([a, b], { id: "foo" })).toEqual([b, a]);
+ });
+
+ it("sorts columns", () => {
+ const a = loc(3, 10);
+ const b = loc(3, 5);
+ expect(sortSelectedLocations([a, b], { id: "foo" })).toEqual([b, a]);
+ });
+
+ it("prioritizes undefined columns", () => {
+ const a = loc(3, 10);
+ const b = loc(3, undefined);
+ expect(sortSelectedLocations([a, b], { id: "foo" })).toEqual([b, a]);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/log.spec.js b/devtools/client/debugger/src/utils/tests/log.spec.js
new file mode 100644
index 0000000000..ea7e4ca4d2
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/log.spec.js
@@ -0,0 +1,35 @@
+/* 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/>. */
+
+import { prefs } from "../prefs";
+import { log } from "../log.js";
+
+let logArgFirst, logArgSecond, logArgArray;
+
+describe("log()", () => {
+ beforeEach(() => {
+ logArgFirst = "my info";
+ logArgSecond = "my other info";
+ logArgArray = [logArgFirst, logArgSecond];
+ global.console = { log: jest.fn() };
+ });
+
+ afterEach(() => {
+ prefs.logging = false;
+ });
+
+ describe("when logging pref is true", () => {
+ it("prints arguments", () => {
+ prefs.logging = true;
+ log(logArgArray);
+
+ expect(global.console.log).toHaveBeenCalledWith(logArgArray);
+ });
+
+ it("does not print by default", () => {
+ log(logArgArray);
+ expect(global.console.log).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/memoize.spec.js b/devtools/client/debugger/src/utils/tests/memoize.spec.js
new file mode 100644
index 0000000000..fb72958516
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/memoize.spec.js
@@ -0,0 +1,48 @@
+/* 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/>. */
+
+import memoize from "../memoize";
+
+const a = { number: 3 };
+const b = { number: 4 };
+const c = { number: 5 };
+const d = { number: 6 };
+
+function add(...numberObjects) {
+ return numberObjects.reduce((prev, cur) => prev + cur.number, 0);
+}
+
+describe("memozie", () => {
+ it("should work for one arg as key", () => {
+ const mAdd = memoize(add);
+ mAdd(a);
+ expect(mAdd(a)).toEqual(3);
+ mAdd(b);
+ expect(mAdd(b)).toEqual(4);
+ });
+
+ it("should only be called once", () => {
+ const spy = jest.fn(() => 2);
+ const mAdd = memoize(spy);
+
+ mAdd(a);
+ mAdd(a);
+ mAdd(a);
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it("should work for two args as key", () => {
+ const mAdd = memoize(add);
+ expect(mAdd(a, b)).toEqual(7);
+ expect(mAdd(a, b)).toEqual(7);
+ expect(mAdd(a, c)).toEqual(8);
+ });
+
+ it("should work with many args as key", () => {
+ const mAdd = memoize(add);
+ expect(mAdd(a, b, c)).toEqual(12);
+ expect(mAdd(a, b, d)).toEqual(13);
+ expect(mAdd(a, b, c)).toEqual(12);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/memoizeLast.spec.js b/devtools/client/debugger/src/utils/tests/memoizeLast.spec.js
new file mode 100644
index 0000000000..a5622510e3
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/memoizeLast.spec.js
@@ -0,0 +1,31 @@
+/* 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/>. */
+
+import { memoizeLast } from "../memoizeLast";
+
+const a = { number: 3 };
+const b = { number: 4 };
+
+function add(...numberObjects) {
+ return numberObjects.reduce((prev, cur) => prev + cur.number, 0);
+}
+
+describe("memozie", () => {
+ it("should re-calculate when a value changes", () => {
+ const mAdd = memoizeLast(add);
+ mAdd(a);
+ expect(mAdd(a)).toEqual(3);
+ mAdd(b);
+ expect(mAdd(b)).toEqual(4);
+ });
+
+ it("should only run once", () => {
+ const mockAdd = jest.fn(add);
+ const mAdd = memoizeLast(mockAdd);
+ mAdd(a);
+ mAdd(a);
+
+ expect(mockAdd.mock.calls[0]).toEqual([{ number: 3 }]);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/path.spec.js b/devtools/client/debugger/src/utils/tests/path.spec.js
new file mode 100644
index 0000000000..58bdf046f0
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/path.spec.js
@@ -0,0 +1,49 @@
+/* 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/>. */
+
+import { basename, dirname, isURL, isAbsolute, join } from "../path";
+
+const fullTestURL = "https://www.example.com/some/endpoint";
+const absoluteTestPath = "/some/absolute/path/to/resource";
+const aTestName = "name";
+
+describe("basename()", () => {
+ it("returns the basename of the path", () => {
+ expect(basename(fullTestURL)).toBe("endpoint");
+ });
+});
+
+describe("dirname()", () => {
+ it("returns the current directory in a path", () => {
+ expect(dirname(fullTestURL)).toBe("https://www.example.com/some");
+ });
+});
+
+describe("isURL()", () => {
+ it("returns true if a string contains characters denoting a scheme", () => {
+ expect(isURL(fullTestURL)).toBe(true);
+ });
+
+ it("returns false if string does not denote a scheme", () => {
+ expect(isURL(absoluteTestPath)).toBe(false);
+ });
+});
+
+describe("isAbsolute()", () => {
+ it("returns true if a string begins with a slash", () => {
+ expect(isAbsolute(absoluteTestPath)).toBe(true);
+ });
+
+ it("returns false if a string does not begin with a slash", () => {
+ expect(isAbsolute(fullTestURL)).toBe(false);
+ });
+});
+
+describe("join()", () => {
+ it("concatenates a base path and a directory name", () => {
+ expect(join(absoluteTestPath, aTestName)).toBe(
+ "/some/absolute/path/to/resource/name"
+ );
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/quick-open.spec.js b/devtools/client/debugger/src/utils/tests/quick-open.spec.js
new file mode 100644
index 0000000000..37d4b99fe6
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/quick-open.spec.js
@@ -0,0 +1,35 @@
+/* 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/>. */
+
+import cases from "jest-in-case";
+import { parseQuickOpenQuery, parseLineColumn } from "../quick-open";
+
+cases(
+ "parseQuickOpenQuery utility",
+ ({ type, query }) => expect(parseQuickOpenQuery(query)).toEqual(type),
+ [
+ { name: "empty query defaults to sources", type: "sources", query: "" },
+ { name: "sources query", type: "sources", query: "test" },
+ { name: "functions query", type: "functions", query: "@test" },
+ { name: "variables query", type: "variables", query: "#test" },
+ { name: "goto line", type: "goto", query: ":30" },
+ { name: "goto line:column", type: "goto", query: ":30:60" },
+ { name: "goto source line", type: "gotoSource", query: "test:30:60" },
+ { name: "shortcuts", type: "shortcuts", query: "?" },
+ ]
+);
+
+cases(
+ "parseLineColumn utility",
+ ({ query, location }) => expect(parseLineColumn(query)).toEqual(location),
+ [
+ { name: "empty query", query: "", location: null },
+ { name: "just line", query: ":30", location: { line: 30 } },
+ {
+ name: "line and column",
+ query: ":30:90",
+ location: { column: 90, line: 30 },
+ },
+ ]
+);
diff --git a/devtools/client/debugger/src/utils/tests/result-list.spec.js b/devtools/client/debugger/src/utils/tests/result-list.spec.js
new file mode 100644
index 0000000000..a0ce0376a1
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/result-list.spec.js
@@ -0,0 +1,32 @@
+/* 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/>. */
+
+import { scrollList } from "../result-list.js";
+
+describe("scrollList", () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
+ /* eslint-disable jest/expect-expect */
+ it("just returns if element not found", () => {
+ const li = document.createElement("li");
+ scrollList([li], 1);
+ });
+ /* eslint-enable jest/expect-expect */
+
+ it("calls scrollIntoView ", () => {
+ const ul = document.createElement("ul");
+ const li = document.createElement("li");
+
+ li.scrollIntoView = jest.fn();
+ ul.appendChild(li);
+
+ scrollList([li], 0);
+
+ jest.runAllTimers();
+
+ expect(li.scrollIntoView).toHaveBeenCalled();
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/source.spec.js b/devtools/client/debugger/src/utils/tests/source.spec.js
new file mode 100644
index 0000000000..484c8ce570
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/source.spec.js
@@ -0,0 +1,367 @@
+/* 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/>. */
+
+import {
+ getFilename,
+ getTruncatedFileName,
+ getFileURL,
+ getDisplayPath,
+ getSourceLineCount,
+ isJavaScript,
+ isDescendantOfRoot,
+ removeThreadActorId,
+ isUrlExtension,
+ getLineText,
+} from "../source.js";
+
+import {
+ makeMockSource,
+ makeMockSourceWithContent,
+ makeMockSourceAndContent,
+ makeMockWasmSourceWithContent,
+ makeMockThread,
+ makeFullfilledMockSourceContent,
+} from "../test-mockup";
+import { isFulfilled } from "../async-value.js";
+
+describe("sources", () => {
+ const unicode = "\u6e2c";
+ const encodedUnicode = encodeURIComponent(unicode);
+
+ describe("getFilename", () => {
+ it("should give us a default of (index)", () => {
+ expect(
+ getFilename(makeMockSource("http://localhost.com:7999/increment/"))
+ ).toBe("(index)");
+ });
+ it("should give us the filename", () => {
+ expect(
+ getFilename(
+ makeMockSource("http://localhost.com:7999/increment/hello.html")
+ )
+ ).toBe("hello.html");
+ });
+ it("should give us the readable Unicode filename if encoded", () => {
+ expect(
+ getFilename(
+ makeMockSource(
+ `http://localhost.com:7999/increment/${encodedUnicode}.html`
+ )
+ )
+ ).toBe(`${unicode}.html`);
+ });
+ it("should give us the filename excluding the query strings", () => {
+ expect(
+ getFilename(
+ makeMockSource(
+ "http://localhost.com:7999/increment/hello.html?query_strings"
+ )
+ )
+ ).toBe("hello.html");
+ });
+ it("should give us the proper filename for pretty files", () => {
+ expect(
+ getFilename(
+ makeMockSource(
+ "http://localhost.com:7999/increment/hello.html:formatted"
+ )
+ )
+ ).toBe("hello.html");
+ });
+ });
+
+ describe("getTruncatedFileName", () => {
+ it("should truncate the file name when it is more than 30 chars", () => {
+ expect(
+ getTruncatedFileName(
+ makeMockSource(
+ "really-really-really-really-really-really-long-name.html"
+ ),
+ "",
+ 30
+ )
+ ).toBe("really-really…long-name.html");
+ });
+ it("should first decode the filename and then truncate it", () => {
+ expect(
+ getTruncatedFileName(
+ makeMockSource(`${encodedUnicode.repeat(30)}.html`),
+ "",
+ 30
+ )
+ ).toBe("測測測測測測測測測測測測測…測測測測測測測測測.html");
+ });
+ });
+
+ describe("getDisplayPath", () => {
+ it("should give us the path for files with same name", () => {
+ const sources = [
+ makeMockSource("http://localhost.com:7999/increment/xyz/hello.html"),
+ makeMockSource("http://localhost.com:7999/increment/abc/hello.html"),
+ makeMockSource("http://localhost.com:7999/increment/hello.html"),
+ ];
+ expect(
+ getDisplayPath(
+ makeMockSource("http://localhost.com:7999/increment/abc/hello.html"),
+ sources
+ )
+ ).toBe("abc");
+ });
+
+ it(`should give us the path for files with same name
+ in directories with same name`, () => {
+ const sources = [
+ makeMockSource(
+ "http://localhost.com:7999/increment/xyz/web/hello.html"
+ ),
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html"
+ ),
+ makeMockSource("http://localhost.com:7999/increment/hello.html"),
+ ];
+ expect(
+ getDisplayPath(
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html"
+ ),
+ sources
+ )
+ ).toBe("abc/web");
+ });
+
+ it("should give no path for files with unique name", () => {
+ const sources = [
+ makeMockSource("http://localhost.com:7999/increment/xyz.html"),
+ makeMockSource("http://localhost.com:7999/increment/abc.html"),
+ makeMockSource("http://localhost.com:7999/increment/hello.html"),
+ ];
+ expect(
+ getDisplayPath(
+ makeMockSource("http://localhost.com:7999/increment/abc/web.html"),
+ sources
+ )
+ ).toBe(undefined);
+ });
+ it("should not show display path for pretty file", () => {
+ const sources = [
+ makeMockSource("http://localhost.com:7999/increment/abc/web/hell.html"),
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html"
+ ),
+ makeMockSource(
+ "http://localhost.com:7999/increment/xyz.html:formatted"
+ ),
+ ];
+ expect(
+ getDisplayPath(
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html:formatted"
+ ),
+ sources
+ )
+ ).toBe(undefined);
+ });
+ it(`should give us the path for files with same name when both
+ are pretty and different path`, () => {
+ const sources = [
+ makeMockSource(
+ "http://localhost.com:7999/increment/xyz/web/hello.html:formatted"
+ ),
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html:formatted"
+ ),
+ makeMockSource(
+ "http://localhost.com:7999/increment/hello.html:formatted"
+ ),
+ ];
+ expect(
+ getDisplayPath(
+ makeMockSource(
+ "http://localhost.com:7999/increment/abc/web/hello.html:formatted"
+ ),
+ sources
+ )
+ ).toBe("abc/web");
+ });
+ });
+
+ describe("getFileURL", () => {
+ it("should give us the file url", () => {
+ expect(
+ getFileURL(
+ makeMockSource("http://localhost.com:7999/increment/hello.html")
+ )
+ ).toBe("http://localhost.com:7999/increment/hello.html");
+ });
+ it("should truncate the file url when it is more than 50 chars", () => {
+ expect(
+ getFileURL(
+ makeMockSource("http://localhost-long.com:7999/increment/hello.html")
+ )
+ ).toBe("…ttp://localhost-long.com:7999/increment/hello.html");
+ });
+ it("should first decode the file URL and then truncate it", () => {
+ expect(
+ getFileURL(makeMockSource(`http://${encodedUnicode.repeat(39)}.html`))
+ ).toBe(`…ttp://${unicode.repeat(39)}.html`);
+ });
+ });
+
+ describe("isJavaScript", () => {
+ it("is not JavaScript", () => {
+ {
+ const source = makeMockSourceAndContent("foo.html", undefined, "");
+ expect(isJavaScript(source, source.content)).toBe(false);
+ }
+ {
+ const source = makeMockSourceAndContent(
+ undefined,
+ undefined,
+ "text/html"
+ );
+ expect(isJavaScript(source, source.content)).toBe(false);
+ }
+ });
+
+ it("is JavaScript", () => {
+ {
+ const source = makeMockSourceAndContent("foo.js");
+ expect(isJavaScript(source, source.content)).toBe(true);
+ }
+ {
+ const source = makeMockSourceAndContent("bar.jsm");
+ expect(isJavaScript(source, source.content)).toBe(true);
+ }
+ {
+ const source = makeMockSourceAndContent(
+ undefined,
+ undefined,
+ "text/javascript"
+ );
+ expect(isJavaScript(source, source.content)).toBe(true);
+ }
+ {
+ const source = makeMockSourceAndContent(
+ undefined,
+ undefined,
+ "application/javascript"
+ );
+ expect(isJavaScript(source, source.content)).toBe(true);
+ }
+ });
+ });
+
+ describe("getSourceLineCount", () => {
+ it("should give us the amount bytes for wasm source", () => {
+ const { content } = makeMockWasmSourceWithContent({
+ binary: "\x00asm\x01\x00\x00\x00",
+ });
+ expect(getSourceLineCount(content.value)).toEqual(8);
+ });
+
+ it("should give us the amout of lines for js source", () => {
+ const { content } = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ "text/javascript",
+ "function foo(){\n}"
+ );
+ if (!content || !isFulfilled(content)) {
+ throw new Error("Unexpected content value");
+ }
+ expect(getSourceLineCount(content.value)).toEqual(2);
+ });
+ });
+
+ describe("isDescendantOfRoot", () => {
+ const threads = [
+ makeMockThread({ actor: "server0.conn1.child1/thread19" }),
+ ];
+
+ it("should detect normal source urls", () => {
+ const source = makeMockSource(
+ "resource://activity-stream/vendor/react.js"
+ );
+ const rootWithoutThreadActor = removeThreadActorId(
+ "resource://activity-stream",
+ threads
+ );
+ expect(isDescendantOfRoot(source, rootWithoutThreadActor)).toBe(true);
+ });
+
+ it("should detect source urls under chrome:// as root", () => {
+ const source = makeMockSource(
+ "chrome://browser/content/contentSearchUI.js"
+ );
+ const rootWithoutThreadActor = removeThreadActorId("chrome://", threads);
+ expect(isDescendantOfRoot(source, rootWithoutThreadActor)).toBe(true);
+ });
+
+ it("should detect source urls if root is a thread actor Id", () => {
+ const source = makeMockSource(
+ "resource://activity-stream/vendor/react-dom.js"
+ );
+ const rootWithoutThreadActor = removeThreadActorId(
+ "server0.conn1.child1/thread19",
+ threads
+ );
+ expect(isDescendantOfRoot(source, rootWithoutThreadActor)).toBe(true);
+ });
+ });
+
+ describe("isUrlExtension", () => {
+ it("should detect mozilla extension", () => {
+ expect(isUrlExtension("moz-extension://id/js/content.js")).toBe(true);
+ });
+ it("should detect chrome extension", () => {
+ expect(isUrlExtension("chrome-extension://id/js/content.js")).toBe(true);
+ });
+ it("should return false for non-extension assets", () => {
+ expect(isUrlExtension("https://example.org/init.js")).toBe(false);
+ });
+ });
+
+ describe("getLineText", () => {
+ it("first line", () => {
+ const text = getLineText(
+ "fake-source",
+ makeFullfilledMockSourceContent("aaa\nbbb\nccc"),
+ 1
+ );
+
+ expect(text).toEqual("aaa");
+ });
+
+ it("last line", () => {
+ const text = getLineText(
+ "fake-source",
+ makeFullfilledMockSourceContent("aaa\nbbb\nccc"),
+ 3
+ );
+
+ expect(text).toEqual("ccc");
+ });
+
+ it("one line", () => {
+ const text = getLineText(
+ "fake-source",
+ makeFullfilledMockSourceContent("aaa"),
+ 1
+ );
+
+ expect(text).toEqual("aaa");
+ });
+
+ it("bad line", () => {
+ const text = getLineText(
+ "fake-source",
+ makeFullfilledMockSourceContent("aaa\nbbb\nccc"),
+
+ 5
+ );
+
+ expect(text).toEqual("");
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/telemetry.spec.js b/devtools/client/debugger/src/utils/tests/telemetry.spec.js
new file mode 100644
index 0000000000..7223641afd
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/telemetry.spec.js
@@ -0,0 +1,13 @@
+/* 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/>. */
+
+import { recordEvent } from "../telemetry";
+
+describe("telemetry.recordEvent()", () => {
+ it("Receives the correct telemetry information", () => {
+ recordEvent("foo", { bar: 1 });
+
+ expect(window.dbg._telemetry.events.foo).toStrictEqual([{ bar: 1 }]);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/text.spec.js b/devtools/client/debugger/src/utils/tests/text.spec.js
new file mode 100644
index 0000000000..5786bc6232
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/text.spec.js
@@ -0,0 +1,20 @@
+/* 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/>. */
+
+import { truncateMiddleText } from "../text";
+
+describe("text", () => {
+ it("should truncate the text in the middle", () => {
+ const sourceText = "this is a very long text and ends here";
+ expect(truncateMiddleText(sourceText, 30)).toMatch(
+ "this is a ver… and ends here"
+ );
+ });
+ it("should keep the text as it is", () => {
+ const sourceText = "this is a short text ends here";
+ expect(truncateMiddleText(sourceText, 30)).toMatch(
+ "this is a short text ends here"
+ );
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/ui.spec.js b/devtools/client/debugger/src/utils/tests/ui.spec.js
new file mode 100644
index 0000000000..aa091e6798
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/ui.spec.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+import { isVisible } from "../ui";
+
+describe("ui", () => {
+ it("should return #mount width", () => {
+ if (!document.body) {
+ throw new Error("no document body");
+ }
+ document.body.innerHTML = "<div id='mount'></div>";
+ expect(isVisible()).toBe(false);
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/url.spec.js b/devtools/client/debugger/src/utils/tests/url.spec.js
new file mode 100644
index 0000000000..d842acf7db
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/url.spec.js
@@ -0,0 +1,89 @@
+/* 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/>. */
+
+import { parse } from "../url";
+
+describe("url", () => {
+ describe("parse", () => {
+ it("parses an absolute URL", () => {
+ const val = parse("http://example.com:8080/path/file.js");
+
+ expect(val.protocol).toBe("http:");
+ expect(val.host).toBe("example.com:8080");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("");
+ expect(val.hash).toBe("");
+ });
+
+ it("parses an absolute URL with query params", () => {
+ const val = parse("http://example.com:8080/path/file.js?param");
+
+ expect(val.protocol).toBe("http:");
+ expect(val.host).toBe("example.com:8080");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("?param");
+ expect(val.hash).toBe("");
+ });
+
+ it("parses an absolute URL with a fragment", () => {
+ const val = parse("http://example.com:8080/path/file.js#hash");
+
+ expect(val.protocol).toBe("http:");
+ expect(val.host).toBe("example.com:8080");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("");
+ expect(val.hash).toBe("#hash");
+ });
+
+ it("parses an absolute URL with query params and a fragment", () => {
+ const val = parse("http://example.com:8080/path/file.js?param#hash");
+
+ expect(val.protocol).toBe("http:");
+ expect(val.host).toBe("example.com:8080");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("?param");
+ expect(val.hash).toBe("#hash");
+ });
+
+ it("parses a partial URL", () => {
+ const val = parse("/path/file.js");
+
+ expect(val.protocol).toBe("");
+ expect(val.host).toBe("");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("");
+ expect(val.hash).toBe("");
+ });
+
+ it("parses a partial URL with query params", () => {
+ const val = parse("/path/file.js?param");
+
+ expect(val.protocol).toBe("");
+ expect(val.host).toBe("");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("?param");
+ expect(val.hash).toBe("");
+ });
+
+ it("parses a partial URL with a fragment", () => {
+ const val = parse("/path/file.js#hash");
+
+ expect(val.protocol).toBe("");
+ expect(val.host).toBe("");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("");
+ expect(val.hash).toBe("#hash");
+ });
+
+ it("parses a partial URL with query params and a fragment", () => {
+ const val = parse("/path/file.js?param#hash");
+
+ expect(val.protocol).toBe("");
+ expect(val.host).toBe("");
+ expect(val.pathname).toBe("/path/file.js");
+ expect(val.search).toBe("?param");
+ expect(val.hash).toBe("#hash");
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/utils.spec.js b/devtools/client/debugger/src/utils/tests/utils.spec.js
new file mode 100644
index 0000000000..f358ffc42a
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/utils.spec.js
@@ -0,0 +1,87 @@
+/* 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/>. */
+
+import { handleError, promisify, endTruncateStr, waitForMs } from "../utils";
+
+describe("handleError()", () => {
+ const testErrorText = "ERROR: ";
+ const testErrorObject = { oh: "noes" };
+
+ beforeEach(() => {
+ global.console = { log: jest.fn() };
+ });
+
+ it("logs error text with error value", () => {
+ handleError(testErrorObject);
+
+ expect(console.log).toHaveBeenCalledWith(testErrorText, testErrorObject);
+ });
+});
+
+describe("promisify()", () => {
+ let testPromise, testContext, testMethod, testArgs;
+
+ beforeEach(() => {
+ testContext = {};
+ testMethod = jest.fn();
+ testArgs = [];
+ });
+
+ it("returns a Promise", () => {
+ testPromise = promisify(testContext, testMethod, testArgs);
+
+ expect(testPromise instanceof Promise).toBe(true);
+ });
+
+ it("applies promisified method", () => {
+ testPromise = promisify(testContext, testMethod, testArgs);
+
+ expect(testMethod).toHaveBeenCalledWith(testArgs, expect.anything());
+ });
+});
+
+describe("endTruncateStr()", () => {
+ let testString;
+ const testSize = 11;
+
+ describe("when the string is larger than the specified size", () => {
+ it("returns an elipsis and characters at the end of the string", () => {
+ testString = "Mozilla Firefox is my favorite web browser";
+
+ expect(endTruncateStr(testString, testSize)).toBe("…web browser");
+ });
+ });
+
+ describe("when the string is not larger than the specified size", () => {
+ it("returns the string unchanged", () => {
+ testString = "Firefox";
+
+ expect(endTruncateStr(testString, testSize)).toBe(testString);
+ });
+ });
+});
+
+describe("waitForMs()", () => {
+ let testPromise;
+ const testMilliseconds = 10;
+
+ beforeEach(() => {
+ global.setTimeout = jest.fn();
+ });
+
+ it("returns a Promise", () => {
+ testPromise = waitForMs(testMilliseconds);
+
+ expect(testPromise instanceof Promise).toBe(true);
+ });
+
+ it("calls setTimeout() on the resolve of the Promise", () => {
+ testPromise = waitForMs(testMilliseconds);
+
+ expect(setTimeout).toHaveBeenCalledWith(
+ expect.anything(),
+ testMilliseconds
+ );
+ });
+});
diff --git a/devtools/client/debugger/src/utils/tests/wasm.spec.js b/devtools/client/debugger/src/utils/tests/wasm.spec.js
new file mode 100644
index 0000000000..e803393c12
--- /dev/null
+++ b/devtools/client/debugger/src/utils/tests/wasm.spec.js
@@ -0,0 +1,96 @@
+/* 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/>. */
+
+import {
+ isWasm,
+ lineToWasmOffset,
+ wasmOffsetToLine,
+ clearWasmStates,
+ renderWasmText,
+} from "../wasm.js";
+
+import { makeMockWasmSourceWithContent } from "../test-mockup";
+
+describe("wasm", () => {
+ // Compiled version of `(module (func (nop)))`
+ const SIMPLE_WASM = {
+ binary:
+ "\x00asm\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01`\x00\x00" +
+ "\x03\x82\x80\x80\x80\x00\x01\x00\x06\x81\x80\x80\x80\x00\x00" +
+ "\n\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x01\v",
+ };
+
+ // malformed binary which contains an unknown operator (\x09) which
+ // should cause the wasm parser to throw.
+ const MALFORMED_SIMPLE_WASM = {
+ binary:
+ "\x00asm\x01\x00\x00\x00\x09\x84\x80\x80\x80\x00\x01`\x00\x00" +
+ "\x03\x82\x80\x80\x80\x00\x01\x00\x06\x81\x80\x80\x80\x00\x00" +
+ "\n\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x01\v",
+ };
+
+ const SIMPLE_WASM_TEXT = `(module
+ (func $func0
+ nop
+ )
+)`;
+ const SIMPLE_WASM_NOP_TEXT_LINE = 2;
+ const SIMPLE_WASM_NOP_OFFSET = 46;
+
+ describe("isWasm", () => {
+ it("should give us the false when wasm text was not registered", () => {
+ const sourceId = "source.0";
+ expect(isWasm(sourceId)).toEqual(false);
+ });
+ it("should give us the true when wasm text was registered", () => {
+ const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
+ renderWasmText(source.id, source.content.value);
+ expect(isWasm(source.id)).toEqual(true);
+ // clear shall remove
+ clearWasmStates();
+ expect(isWasm(source.id)).toEqual(false);
+ });
+ });
+
+ describe("renderWasmText", () => {
+ it("render simple wasm", () => {
+ const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
+ const lines = renderWasmText(source.id, source.content.value);
+ expect(lines.join("\n")).toEqual(SIMPLE_WASM_TEXT);
+ clearWasmStates();
+ });
+
+ it("should return error information when the parser throws", () => {
+ const source = makeMockWasmSourceWithContent(MALFORMED_SIMPLE_WASM);
+ const lines = renderWasmText(source.id, source.content.value);
+ expect(lines.join("\n")).toEqual(
+ "Error occured during wast conversion : Unknown operator: 6"
+ );
+ clearWasmStates();
+ });
+ });
+
+ describe("lineToWasmOffset", () => {
+ // Test data sanity check: checking if 'nop' is found in the SIMPLE_WASM.
+ expect(SIMPLE_WASM.binary[SIMPLE_WASM_NOP_OFFSET]).toEqual("\x01");
+
+ it("get simple wasm nop offset", () => {
+ const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
+ renderWasmText(source.id, source.content.value);
+ const offset = lineToWasmOffset(source.id, SIMPLE_WASM_NOP_TEXT_LINE);
+ expect(offset).toEqual(SIMPLE_WASM_NOP_OFFSET);
+ clearWasmStates();
+ });
+ });
+
+ describe("wasmOffsetToLine", () => {
+ it("get simple wasm nop line", () => {
+ const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
+ renderWasmText(source.id, source.content.value);
+ const line = wasmOffsetToLine(source.id, SIMPLE_WASM_NOP_OFFSET);
+ expect(line).toEqual(SIMPLE_WASM_NOP_TEXT_LINE);
+ clearWasmStates();
+ });
+ });
+});