diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /devtools/client/debugger/src/utils/tests | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/utils/tests')
29 files changed, 2028 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..796c752453 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/DevToolsUtils.spec.js @@ -0,0 +1,43 @@ +/* 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/>. */ + +// @flow + +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..68dd5fad1c --- /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 exxpression should wrap an expression 1`] = ` +"try { + foo +} catch (e) { + e +}" +`; + +exports[`expressions wrap exxpression should wrap expression with a comment 1`] = ` +"try { + foo // yo yo +} catch (e) { + e +}" +`; + +exports[`expressions wrap exxpression 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/__snapshots__/project-search.spec.js.snap b/devtools/client/debugger/src/utils/tests/__snapshots__/project-search.spec.js.snap new file mode 100644 index 0000000000..e022edd6ee --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/__snapshots__/project-search.spec.js.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`project search - highlightMatches simple 1`] = ` +<span + className="line-value" +> + <span + className="line-match" + > + This is a sample + </span> + <span + className="query-match" + > + sentence + </span> + <span + className="line-match" + > + + </span> +</span> +`; 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..5a3ff546b4 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/assert.spec.js @@ -0,0 +1,40 @@ +/* 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/>. */ + +// @flow + +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)); + }); + }); + + describe("when not in Node test", () => { + it("does not throw an Error", () => { + process.env.NODE_ENV = "production"; + expect(() => assert(false, testAssertMessage)).not.toThrow(); + delete process.env.NODE_ENV; + }); + }); +}); 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..0f00ae4fda --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/ast.spec.js @@ -0,0 +1,36 @@ +/* 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/>. */ + +// @flow + +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..57fda592d3 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/build-query.spec.js @@ -0,0 +1,259 @@ +/* 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/>. */ + +// @flow + +import { escapeRegExp } from "lodash"; +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(escapeRegExp("(?!\\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(escapeRegExp("(?!\\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..3f804a817d --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/clipboard.spec.js @@ -0,0 +1,47 @@ +/* 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/>. */ + +// @flow + +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..c2760c2d15 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/expressions.spec.js @@ -0,0 +1,64 @@ +/* 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/>. */ + +// @flow + +import { wrapExpression, getValue } from "../expressions"; +import { makeMockExpression } from "../test-mockup"; + +function createError(type, preview) { + return makeMockExpression({ + result: { getGrip: () => ({ class: type, isError: true, preview }) }, + }); +} + +describe("expressions", () => { + describe("wrap exxpression", () => { + 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)", () => { + expect( + getValue(createError("ReferenceError", { name: "ReferenceError" })) + ).toEqual({ + unavailable: true, + }); + }); + + it("Errors messages should be shown", () => { + expect( + getValue(createError("Error", { name: "Foo", message: "YO" })) + ).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..abb65b47c3 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/function.spec.js @@ -0,0 +1,63 @@ +/* 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/>. */ + +// @flow + +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, symbols); + expect(text).toMatchSnapshot(); + }); + + it("finds function signature", () => { + const source = populateOriginalSource("func"); + const symbols = getSymbols(source.id); + + const text = findFunctionText(13, source, symbols); + expect(text).toMatchSnapshot(); + }); + + it("misses function closing brace", () => { + const source = populateOriginalSource("func"); + const symbols = getSymbols(source.id); + + const text = findFunctionText(15, source, 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, symbols); + expect(text).toMatchSnapshot(); + }); + + it("finds class function", () => { + const source = populateOriginalSource("func"); + const symbols = getSymbols(source.id); + + const text = findFunctionText(33, source, symbols); + expect(text).toMatchSnapshot(); + }); + + it("cant find function", () => { + const source = populateOriginalSource("func"); + const symbols = getSymbols(source.id); + + const text = findFunctionText(20, source, 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..3681f59707 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/indentation.spec.js @@ -0,0 +1,63 @@ +/* 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/>. */ + +// @flow + +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..b4f73669ab --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/isMinified.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/>. */ + +// @flow + +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)).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..645a1054bd --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/log.spec.js @@ -0,0 +1,37 @@ +/* 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/>. */ + +// @flow + +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..309e12c88d --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/memoize.spec.js @@ -0,0 +1,50 @@ +/* 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/>. */ + +// @flow + +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..53426c8aa4 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/memoizeLast.spec.js @@ -0,0 +1,33 @@ +/* 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/>. */ + +// @flow + +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..c691bb1ebf --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/path.spec.js @@ -0,0 +1,51 @@ +/* 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/>. */ + +// @flow + +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/project-search.spec.js b/devtools/client/debugger/src/utils/tests/project-search.spec.js new file mode 100644 index 0000000000..62c1c58aeb --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/project-search.spec.js @@ -0,0 +1,24 @@ +/* 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/>. */ + +// @flow + +import { highlightMatches } from "../project-search"; + +describe("project search - highlightMatches", () => { + it("simple", () => { + const lineMatch = { + type: "MATCH", + value: "This is a sample sentence", + line: 1, + column: 17, + matchIndex: 17, + match: "sentence", + sourceId: "source", + text: "text", + }; + + expect(highlightMatches(lineMatch)).toMatchSnapshot(); + }); +}); 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..517a7f53ab --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/quick-open.spec.js @@ -0,0 +1,37 @@ +/* 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/>. */ + +// @flow + +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: undefined }, + { 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..d72b4f42a2 --- /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/>. */ + +// @flow + +import { scrollList } from "../result-list.js"; + +describe("scrollList", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + it("just returns if element not found", () => { + const li = document.createElement("li"); + scrollList([li], 1); + }); + + it("calls scrollIntoView ", () => { + const ul = document.createElement("ul"); + const li = document.createElement("li"); + + (li: any).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..86d21c5997 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/source.spec.js @@ -0,0 +1,602 @@ +/* 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/>. */ + +// @flow + +import { + getFilename, + getTruncatedFileName, + getFileURL, + getDisplayPath, + getMode, + getSourceLineCount, + isThirdParty, + isJavaScript, + underRoot, + isUrlExtension, + isExtensionDirectoryPath, + getLineText, +} from "../source.js"; + +import { + makeMockSource, + makeMockSourceWithContent, + makeMockSourceAndContent, + makeMockWasmSourceWithContent, + makeMockThread, + makeFullfilledMockSourceContent, +} from "../test-mockup"; +import { isFulfilled } from "../async-value.js"; + +import type { Source } from "../../types"; + +const defaultSymbolDeclarations = { + classes: [], + functions: [], + memberExpressions: [], + callExpressions: [], + objectProperties: [], + identifiers: [], + imports: [], + comments: [], + literals: [], + hasJsx: false, + hasTypes: false, + loading: false, + framework: undefined, +}; + +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: Source[] = [ + 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: Source[] = [ + 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: Source[] = [ + 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: Source[] = [ + 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: Source[] = [ + 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("isThirdParty", () => { + it("node_modules", () => { + expect(isThirdParty(makeMockSource("/node_modules/foo.js"))).toBe(true); + }); + + it("bower_components", () => { + expect(isThirdParty(makeMockSource("/bower_components/foo.js"))).toBe( + true + ); + }); + + it("not third party", () => { + expect(isThirdParty(makeMockSource("/bar/foo.js"))).toBe(false); + }); + }); + + describe("getMode", () => { + it("//@flow", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/javascript", + "// @flow" + ); + expect(getMode(source, source.content)).toEqual({ + name: "javascript", + typescript: true, + }); + }); + + it("/* @flow */", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/javascript", + " /* @flow */" + ); + expect(getMode(source, source.content)).toEqual({ + name: "javascript", + typescript: true, + }); + }); + + it("mixed html", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "", + " <html" + ); + expect(getMode(source, source.content)).toEqual({ name: "htmlmixed" }); + }); + + it("elm", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/x-elm", + 'main = text "Hello, World!"' + ); + expect(getMode(source, source.content)).toEqual({ name: "elm" }); + }); + + it("returns jsx if contentType jsx is given", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/jsx", + "<h1></h1>" + ); + expect(getMode(source, source.content)).toEqual({ name: "jsx" }); + }); + + it("returns jsx if sourceMetaData says it's a react component", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "", + "<h1></h1>" + ); + expect( + getMode(source, source.content, { + ...defaultSymbolDeclarations, + hasJsx: true, + }) + ).toEqual({ name: "jsx" }); + }); + + it("returns jsx if the fileExtension is .jsx", () => { + const source = makeMockSourceAndContent( + "myComponent.jsx", + undefined, + "", + "<h1></h1>" + ); + expect(getMode(source, source.content)).toEqual({ name: "jsx" }); + }); + + it("returns text/x-haxe if the file extension is .hx", () => { + const source = makeMockSourceAndContent( + "myComponent.hx", + undefined, + "", + "function foo(){}" + ); + expect(getMode(source, source.content)).toEqual({ name: "text/x-haxe" }); + }); + + it("typescript", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/typescript", + "function foo(){}" + ); + expect(getMode(source, source.content)).toEqual({ + name: "javascript", + typescript: true, + }); + }); + + it("typescript-jsx", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/typescript-jsx", + "<h1></h1>" + ); + expect(getMode(source, source.content).base).toEqual({ + name: "javascript", + typescript: true, + }); + }); + + it("cross-platform clojure(script) with reader conditionals", () => { + const source = makeMockSourceAndContent( + "my-clojurescript-source-with-reader-conditionals.cljc", + undefined, + "text/x-clojure", + "(defn str->int [s] " + + " #?(:clj (java.lang.Integer/parseInt s) " + + " :cljs (js/parseInt s)))" + ); + expect(getMode(source, source.content)).toEqual({ name: "clojure" }); + }); + + it("clojurescript", () => { + const source = makeMockSourceAndContent( + "my-clojurescript-source.cljs", + undefined, + "text/x-clojurescript", + "(+ 1 2 3)" + ); + expect(getMode(source, source.content)).toEqual({ name: "clojure" }); + }); + + it("coffeescript", () => { + const source = makeMockSourceAndContent( + undefined, + undefined, + "text/coffeescript", + "x = (a) -> 3" + ); + expect(getMode(source, source.content)).toEqual({ name: "coffeescript" }); + }); + + it("wasm", () => { + const source = makeMockWasmSourceWithContent({ + binary: "\x00asm\x01\x00\x00\x00", + }); + expect(getMode(source, source.content.value)).toEqual({ name: "text" }); + }); + + it("marko", () => { + const source = makeMockSourceAndContent( + "http://localhost.com:7999/increment/sometestfile.marko", + undefined, + "does not matter", + "function foo(){}" + ); + expect(getMode(source, source.content)).toEqual({ name: "javascript" }); + }); + + it("es6", () => { + const source = makeMockSourceAndContent( + "http://localhost.com:7999/increment/sometestfile.es6", + undefined, + "does not matter", + "function foo(){}" + ); + expect(getMode(source, source.content)).toEqual({ name: "javascript" }); + }); + + it("vue", () => { + const source = makeMockSourceAndContent( + "http://localhost.com:7999/increment/sometestfile.vue?query=string", + undefined, + "does not matter", + "function foo(){}" + ); + expect(getMode(source, source.content)).toEqual({ name: "javascript" }); + }); + }); + + 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("underRoot", () => { + const threads = [ + makeMockThread({ actor: "server0.conn1.child1/thread19" }), + ]; + + it("should detect normal source urls", () => { + const source = makeMockSource( + "resource://activity-stream/vendor/react.js" + ); + expect(underRoot(source, "resource://activity-stream", threads)).toBe( + true + ); + }); + + it("should detect source urls under chrome:// as root", () => { + const source = makeMockSource( + "chrome://browser/content/contentSearchUI.js" + ); + expect(underRoot(source, "chrome://", threads)).toBe(true); + }); + + it("should detect source urls if root is a thread actor Id", () => { + const source = makeMockSource( + "resource://activity-stream/vendor/react-dom.js" + ); + expect(underRoot(source, "server0.conn1.child1/thread19", threads)).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("isExtensionDirectoryPath", () => { + it("should detect mozilla extension directory", () => { + expect(isExtensionDirectoryPath("moz-extension://id")).toBe(true); + }); + it("should detect chrome extension directory", () => { + expect(isExtensionDirectoryPath("chrome-extension://id")).toBe(true); + }); + it("should return false for child file within the extension directory", () => { + expect(isExtensionDirectoryPath("moz-extension://id/js/content.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..d23d4facce --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/telemetry.spec.js @@ -0,0 +1,36 @@ +/* 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/>. */ + +// @flow + +jest.mock("devtools/client/shared/telemetry", () => { + function MockTelemetry() {} + MockTelemetry.prototype.recordEvent = jest.fn(); + + return MockTelemetry; +}); + +// $FlowIgnore +const Telemetry = require("devtools/client/shared/telemetry"); + +import { recordEvent } from "../telemetry"; + +const telemetry = new Telemetry(); + +describe("telemetry.recordEvent()", () => { + it("Receives the correct telemetry information", () => { + recordEvent("foo", { bar: 1 }); + + expect(telemetry.recordEvent).toHaveBeenCalledWith( + "foo", + "debugger", + null, + { + // eslint-disable-next-line camelcase + session_id: -1, + 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..52d1ca1b98 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/text.spec.js @@ -0,0 +1,22 @@ +/* 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/>. */ + +// @flow + +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..1e3baaf4a4 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/ui.spec.js @@ -0,0 +1,17 @@ +/* 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/>. */ + +// @flow + +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..ed3749a4b0 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/url.spec.js @@ -0,0 +1,100 @@ +/* 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/>. */ + +// @flow + +import { stripQuery, parse } from "../url"; + +describe("url", () => { + describe("stripQuery", () => { + it("strips properly", () => { + expect(stripQuery("/file/path")).toBe("/file/path"); + expect(stripQuery("/file/path?param")).toBe("/file/path"); + expect(stripQuery("/file/path#hash")).toBe("/file/path#hash"); + expect(stripQuery("/file/path?param#hash")).toBe("/file/path#hash"); + }); + }); + + 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..342adee549 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/utils.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/>. */ + +// @flow + +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..f3509a1a62 --- /dev/null +++ b/devtools/client/debugger/src/utils/tests/wasm.spec.js @@ -0,0 +1,79 @@ +/* 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/>. */ + +// @flow + +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", + }; + 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(); + }); + }); + + 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(); + }); + }); +}); |