summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/editor/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/debugger/src/utils/editor/tests
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/utils/editor/tests')
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/__snapshots__/create-editor.spec.js.snap60
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/create-editor.spec.js25
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/editor.spec.js203
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/get-expression.spec.js160
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/get-token-location.spec.js31
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/source-documents.spec.js215
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/source-search.spec.js182
7 files changed, 876 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/editor/tests/__snapshots__/create-editor.spec.js.snap b/devtools/client/debugger/src/utils/editor/tests/__snapshots__/create-editor.spec.js.snap
new file mode 100644
index 0000000000..f5bba6cd3e
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/__snapshots__/create-editor.spec.js.snap
@@ -0,0 +1,60 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`createEditor Adds codeFolding 1`] = `
+Object {
+ "cursorBlinkRate": 530,
+ "enableCodeFolding": true,
+ "extraKeys": Object {
+ "Cmd-F": false,
+ "Cmd-G": false,
+ "Ctrl-F": false,
+ "Ctrl-G": false,
+ "Esc": false,
+ },
+ "foldGutter": true,
+ "gutters": Array [
+ "breakpoints",
+ "hit-markers",
+ "CodeMirror-linenumbers",
+ "CodeMirror-foldgutter",
+ ],
+ "lineNumbers": true,
+ "lineWrapping": false,
+ "matchBrackets": true,
+ "mode": "javascript",
+ "readOnly": true,
+ "showAnnotationRuler": true,
+ "styleActiveLine": false,
+ "theme": "mozilla",
+ "value": " ",
+}
+`;
+
+exports[`createEditor Returns a SourceEditor 1`] = `
+Object {
+ "cursorBlinkRate": 530,
+ "enableCodeFolding": false,
+ "extraKeys": Object {
+ "Cmd-F": false,
+ "Cmd-G": false,
+ "Ctrl-F": false,
+ "Ctrl-G": false,
+ "Esc": false,
+ },
+ "foldGutter": false,
+ "gutters": Array [
+ "breakpoints",
+ "hit-markers",
+ "CodeMirror-linenumbers",
+ ],
+ "lineNumbers": true,
+ "lineWrapping": false,
+ "matchBrackets": true,
+ "mode": "javascript",
+ "readOnly": true,
+ "showAnnotationRuler": true,
+ "styleActiveLine": false,
+ "theme": "mozilla",
+ "value": " ",
+}
+`;
diff --git a/devtools/client/debugger/src/utils/editor/tests/create-editor.spec.js b/devtools/client/debugger/src/utils/editor/tests/create-editor.spec.js
new file mode 100644
index 0000000000..38e7241b2e
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/create-editor.spec.js
@@ -0,0 +1,25 @@
+/* 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 { createEditor } from "../create-editor";
+import SourceEditor from "../source-editor";
+
+import { features } from "../../prefs";
+
+describe("createEditor", () => {
+ test("Returns a SourceEditor", () => {
+ const editor = createEditor();
+ expect(editor).toBeInstanceOf(SourceEditor);
+ expect(editor.opts).toMatchSnapshot();
+ expect(editor.opts.gutters).not.toContain("CodeMirror-foldgutter");
+ });
+
+ test("Adds codeFolding", () => {
+ features.codeFolding = true;
+ const editor = createEditor();
+ expect(editor).toBeInstanceOf(SourceEditor);
+ expect(editor.opts).toMatchSnapshot();
+ expect(editor.opts.gutters).toContain("CodeMirror-foldgutter");
+ });
+});
diff --git a/devtools/client/debugger/src/utils/editor/tests/editor.spec.js b/devtools/client/debugger/src/utils/editor/tests/editor.spec.js
new file mode 100644
index 0000000000..d657437b19
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/editor.spec.js
@@ -0,0 +1,203 @@
+/* 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 {
+ toEditorLine,
+ toEditorPosition,
+ toEditorRange,
+ toSourceLine,
+ scrollToColumn,
+ markText,
+ lineAtHeight,
+ getSourceLocationFromMouseEvent,
+ forEachLine,
+ removeLineClass,
+ clearLineClass,
+ getTextForLine,
+ getCursorLine,
+} from "../index";
+
+import { makeMockSource } from "../../test-mockup";
+
+describe("toEditorLine", () => {
+ it("returns an editor line", () => {
+ const testId = "test-123";
+ const line = 30;
+ expect(toEditorLine(testId, line)).toEqual(29);
+ });
+});
+
+describe("toEditorPosition", () => {
+ it("returns an editor position", () => {
+ const loc = { source: { id: "source" }, line: 100, column: 25 };
+ expect(toEditorPosition(loc)).toEqual({
+ line: 99,
+ column: 25,
+ });
+ });
+});
+
+describe("toEditorRange", () => {
+ it("returns an editor range", () => {
+ const testId = "test-123";
+ const loc = {
+ start: { source: { id: testId }, line: 100, column: 25 },
+ end: { source: { id: testId }, line: 200, column: 0 },
+ };
+ expect(toEditorRange(testId, loc)).toEqual({
+ start: { line: 99, column: 25 },
+ end: { line: 199, column: 0 },
+ });
+ });
+});
+
+describe("toSourceLine", () => {
+ it("returns a source line", () => {
+ const testId = "test-123";
+ const line = 30;
+ expect(toSourceLine(testId, line)).toEqual(31);
+ });
+});
+
+const codeMirror = {
+ doc: {
+ iter: jest.fn((_, __, cb) => cb()),
+ },
+ lineCount: jest.fn(() => 100),
+ getLine: jest.fn(() => "something"),
+ getCursor: jest.fn(() => ({ line: 3 })),
+ getScrollerElement: jest.fn(() => ({
+ offsetWidth: 100,
+ offsetHeight: 100,
+ })),
+ getScrollInfo: () => ({
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ clientHeight: 100,
+ clientWidth: 100,
+ }),
+ removeLineClass: jest.fn(),
+ operation: jest.fn(cb => cb()),
+ charCoords: jest.fn(() => ({
+ top: 100,
+ right: 50,
+ bottom: 100,
+ left: 50,
+ })),
+ coordsChar: jest.fn(() => ({ line: 6, ch: 30 })),
+ lineAtHeight: jest.fn(() => 300),
+ markText: jest.fn(),
+ scrollTo: jest.fn(),
+ defaultCharWidth: jest.fn(() => 8),
+ defaultTextHeight: jest.fn(() => 16),
+};
+
+const editor = { codeMirror };
+
+describe("scrollToColumn", () => {
+ it("calls codemirror APIs charCoords, getScrollerElement, scrollTo", () => {
+ scrollToColumn(codeMirror, 60, 123);
+ expect(codeMirror.charCoords).toHaveBeenCalledWith(
+ { line: 60, ch: 123 },
+ "local"
+ );
+ expect(codeMirror.scrollTo).toHaveBeenCalledWith(0, 50);
+ });
+});
+
+describe("markText", () => {
+ it("calls codemirror API markText & returns marker", () => {
+ const loc = {
+ start: { line: 10, column: 0 },
+ end: { line: 30, column: 50 },
+ };
+ markText(editor, "test-123", loc);
+ expect(codeMirror.markText).toHaveBeenCalledWith(
+ { ch: loc.start.column, line: loc.start.line },
+ { ch: loc.end.column, line: loc.end.line },
+ { className: "test-123" }
+ );
+ });
+});
+
+describe("lineAtHeight", () => {
+ it("calls codemirror API lineAtHeight", () => {
+ const e = { clientX: 30, clientY: 60 };
+ expect(lineAtHeight(editor, "test-123", e)).toEqual(301);
+ expect(editor.codeMirror.lineAtHeight).toHaveBeenCalledWith(e.clientY);
+ });
+});
+
+describe("getSourceLocationFromMouseEvent", () => {
+ it("calls codemirror API coordsChar & returns location", () => {
+ const source = makeMockSource(undefined, "test-123");
+ const e = { clientX: 30, clientY: 60 };
+ expect(getSourceLocationFromMouseEvent(editor, source, e)).toEqual({
+ source,
+ sourceId: source.id,
+ line: 7,
+ column: 31,
+ sourceActorId: undefined,
+ sourceActor: null,
+ sourceUrl: "",
+ });
+ expect(editor.codeMirror.coordsChar).toHaveBeenCalledWith({
+ left: 30,
+ top: 60,
+ });
+ });
+});
+
+describe("forEachLine", () => {
+ it("calls codemirror API operation && doc.iter across a doc", () => {
+ const test = jest.fn();
+ forEachLine(codeMirror, test);
+ expect(codeMirror.operation).toHaveBeenCalled();
+ expect(codeMirror.doc.iter).toHaveBeenCalledWith(0, 100, test);
+ });
+});
+
+describe("removeLineClass", () => {
+ it("calls codemirror API removeLineClass", () => {
+ const line = 3;
+ const className = "test-class";
+ removeLineClass(codeMirror, line, className);
+ expect(codeMirror.removeLineClass).toHaveBeenCalledWith(
+ line,
+ "wrap",
+ className
+ );
+ });
+});
+
+describe("clearLineClass", () => {
+ it("Uses forEachLine & removeLineClass to clear class on all lines", () => {
+ codeMirror.operation.mockClear();
+ codeMirror.doc.iter.mockClear();
+ codeMirror.removeLineClass.mockClear();
+ clearLineClass(codeMirror, "test-class");
+ expect(codeMirror.operation).toHaveBeenCalled();
+ expect(codeMirror.doc.iter).toHaveBeenCalledWith(
+ 0,
+ 100,
+ expect.any(Function)
+ );
+ expect(codeMirror.removeLineClass).toHaveBeenCalled();
+ });
+});
+
+describe("getTextForLine", () => {
+ it("calls codemirror API getLine & returns line text", () => {
+ getTextForLine(codeMirror, 3);
+ expect(codeMirror.getLine).toHaveBeenCalledWith(2);
+ });
+});
+describe("getCursorLine", () => {
+ it("calls codemirror API getCursor & returns line number", () => {
+ getCursorLine(codeMirror);
+ expect(codeMirror.getCursor).toHaveBeenCalled();
+ });
+});
diff --git a/devtools/client/debugger/src/utils/editor/tests/get-expression.spec.js b/devtools/client/debugger/src/utils/editor/tests/get-expression.spec.js
new file mode 100644
index 0000000000..65ab5152f6
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/get-expression.spec.js
@@ -0,0 +1,160 @@
+/* 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 CodeMirror from "codemirror";
+import { getExpressionFromCoords } from "../get-expression";
+
+describe("get-expression", () => {
+ let isCreateTextRangeDefined;
+
+ beforeAll(() => {
+ if (document.body.createTextRange) {
+ isCreateTextRangeDefined = true;
+ } else {
+ isCreateTextRangeDefined = false;
+ // CodeMirror needs createTextRange
+ // https://discuss.codemirror.net/t/working-in-jsdom-or-node-js-natively/138/5
+ document.body.createTextRange = () => ({
+ getBoundingClientRect: jest.fn(),
+ getClientRects: () => ({}),
+ });
+ }
+ });
+
+ afterAll(() => {
+ if (!isCreateTextRangeDefined) {
+ delete document.body.createTextRange;
+ }
+ });
+
+ describe("getExpressionFromCoords", () => {
+ it("returns null when location.line is greater than the lineCount", () => {
+ const cm = CodeMirror(document.body, {
+ value: "let Line1;\n" + "let Line2;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 3,
+ column: 1,
+ });
+ expect(result).toBeNull();
+ });
+
+ it("gets the expression using CodeMirror.getTokenAt", () => {
+ const codemirrorMock = {
+ lineCount: () => 100,
+ getTokenAt: jest.fn(() => ({ start: 0, end: 0 })),
+ doc: {
+ getLine: () => "",
+ },
+ };
+ getExpressionFromCoords(codemirrorMock, { line: 1, column: 1 });
+ expect(codemirrorMock.getTokenAt).toHaveBeenCalled();
+ });
+
+ it("requests the correct line and column from codeMirror", () => {
+ const codemirrorMock = {
+ lineCount: () => 100,
+ getTokenAt: jest.fn(() => ({ start: 0, end: 1 })),
+ doc: {
+ getLine: jest.fn(() => ""),
+ },
+ };
+ getExpressionFromCoords(codemirrorMock, { line: 20, column: 5 });
+ // getExpressionsFromCoords uses one based line indexing
+ // CodeMirror uses zero based line indexing
+ expect(codemirrorMock.getTokenAt).toHaveBeenCalledWith({
+ line: 19,
+ ch: 5,
+ });
+ expect(codemirrorMock.doc.getLine).toHaveBeenCalledWith(19);
+ });
+
+ it("when called with column 0 returns null", () => {
+ const cm = CodeMirror(document.body, {
+ value: "foo bar;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 1,
+ column: 0,
+ });
+ expect(result).toBeNull();
+ });
+
+ it("gets the expression when first token on the line", () => {
+ const cm = CodeMirror(document.body, {
+ value: "foo bar;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 1,
+ column: 1,
+ });
+ if (!result) {
+ throw new Error("no result");
+ }
+ expect(result.expression).toEqual("foo");
+ expect(result.location.start).toEqual({ line: 1, column: 0 });
+ expect(result.location.end).toEqual({ line: 1, column: 3 });
+ });
+
+ it("includes previous tokens in the expression", () => {
+ const cm = CodeMirror(document.body, {
+ value: "foo.bar;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 1,
+ column: 5,
+ });
+ if (!result) {
+ throw new Error("no result");
+ }
+ expect(result.expression).toEqual("foo.bar");
+ expect(result.location.start).toEqual({ line: 1, column: 0 });
+ expect(result.location.end).toEqual({ line: 1, column: 7 });
+ });
+
+ it("includes multiple previous tokens in the expression", () => {
+ const cm = CodeMirror(document.body, {
+ value: "foo.bar.baz;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 1,
+ column: 10,
+ });
+ if (!result) {
+ throw new Error("no result");
+ }
+ expect(result.expression).toEqual("foo.bar.baz");
+ expect(result.location.start).toEqual({ line: 1, column: 0 });
+ expect(result.location.end).toEqual({ line: 1, column: 11 });
+ });
+
+ it("does not include tokens not part of the expression", () => {
+ const cm = CodeMirror(document.body, {
+ value: "foo bar.baz;\n",
+ mode: "javascript",
+ });
+
+ const result = getExpressionFromCoords(cm, {
+ line: 1,
+ column: 10,
+ });
+ if (!result) {
+ throw new Error("no result");
+ }
+ expect(result.expression).toEqual("bar.baz");
+ expect(result.location.start).toEqual({ line: 1, column: 4 });
+ expect(result.location.end).toEqual({ line: 1, column: 11 });
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/editor/tests/get-token-location.spec.js b/devtools/client/debugger/src/utils/editor/tests/get-token-location.spec.js
new file mode 100644
index 0000000000..c4aa277f26
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/get-token-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 { getTokenLocation } from "../get-token-location";
+
+describe("getTokenLocation", () => {
+ const codemirror = {
+ coordsChar: jest.fn(() => ({
+ line: 1,
+ ch: "C",
+ })),
+ };
+ const token = {
+ getBoundingClientRect() {
+ return {
+ left: 10,
+ top: 20,
+ width: 10,
+ height: 10,
+ };
+ },
+ };
+ it("calls into codeMirror", () => {
+ getTokenLocation(codemirror, token);
+ expect(codemirror.coordsChar).toHaveBeenCalledWith({
+ left: 15,
+ top: 25,
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/editor/tests/source-documents.spec.js b/devtools/client/debugger/src/utils/editor/tests/source-documents.spec.js
new file mode 100644
index 0000000000..9d4b42e263
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/source-documents.spec.js
@@ -0,0 +1,215 @@
+/* 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 { getMode } from "../source-documents.js";
+
+import {
+ makeMockSourceWithContent,
+ makeMockWasmSourceWithContent,
+} from "../../test-mockup";
+
+const defaultSymbolDeclarations = {
+ classes: [],
+ functions: [],
+ memberExpressions: [],
+ callExpressions: [],
+ objectProperties: [],
+ identifiers: [],
+ imports: [],
+ comments: [],
+ literals: [],
+ hasJsx: false,
+ hasTypes: false,
+ framework: undefined,
+};
+
+describe("source-documents", () => {
+ describe("getMode", () => {
+ it("// ", () => {
+ const source = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ "text/javascript",
+ "// @flow"
+ );
+ expect(getMode(source, source.content)).toEqual({
+ name: "javascript",
+ typescript: true,
+ });
+ });
+
+ it("/* @flow */", () => {
+ const source = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ "text/javascript",
+ " /* @flow */"
+ );
+ expect(getMode(source, source.content)).toEqual({
+ name: "javascript",
+ typescript: true,
+ });
+ });
+
+ it("mixed html", () => {
+ const source = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ "",
+ " <html"
+ );
+ expect(getMode(source, source.content)).toEqual({ name: "htmlmixed" });
+ });
+
+ it("elm", () => {
+ const source = makeMockSourceWithContent(
+ 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 = makeMockSourceWithContent(
+ 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 = makeMockSourceWithContent(
+ 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 = makeMockSourceWithContent(
+ "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 = makeMockSourceWithContent(
+ "myComponent.hx",
+ undefined,
+ "",
+ "function foo(){}"
+ );
+ expect(getMode(source, source.content)).toEqual({ name: "text/x-haxe" });
+ });
+
+ it("typescript", () => {
+ const source = makeMockSourceWithContent(
+ undefined,
+ undefined,
+ "text/typescript",
+ "function foo(){}"
+ );
+ expect(getMode(source, source.content)).toEqual({
+ name: "javascript",
+ typescript: true,
+ });
+ });
+
+ it("typescript-jsx", () => {
+ const source = makeMockSourceWithContent(
+ 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 = makeMockSourceWithContent(
+ "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 = makeMockSourceWithContent(
+ "my-clojurescript-source.cljs",
+ undefined,
+ "text/x-clojurescript",
+ "(+ 1 2 3)"
+ );
+ expect(getMode(source, source.content)).toEqual({ name: "clojure" });
+ });
+
+ it("coffeescript", () => {
+ const source = makeMockSourceWithContent(
+ 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 = makeMockSourceWithContent(
+ "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 = makeMockSourceWithContent(
+ "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 = makeMockSourceWithContent(
+ "http://localhost.com:7999/increment/sometestfile.vue?query=string",
+ undefined,
+ "does not matter",
+ "function foo(){}"
+ );
+ expect(getMode(source, source.content)).toEqual({ name: "javascript" });
+ });
+ });
+});
diff --git a/devtools/client/debugger/src/utils/editor/tests/source-search.spec.js b/devtools/client/debugger/src/utils/editor/tests/source-search.spec.js
new file mode 100644
index 0000000000..33f479766a
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/tests/source-search.spec.js
@@ -0,0 +1,182 @@
+/* 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 {
+ find,
+ searchSourceForHighlight,
+ getMatchIndex,
+ removeOverlay,
+} from "../source-search";
+
+const getCursor = jest.fn(() => ({ line: 90, ch: 54 }));
+const cursor = {
+ find: jest.fn(),
+ from: jest.fn(),
+ to: jest.fn(),
+};
+const getSearchCursor = jest.fn(() => cursor);
+const modifiers = {
+ caseSensitive: false,
+ regexMatch: false,
+ wholeWord: false,
+};
+
+const getCM = () => ({
+ operation: jest.fn(cb => cb()),
+ addOverlay: jest.fn(),
+ removeOverlay: jest.fn(),
+ getCursor,
+ getSearchCursor,
+ firstLine: jest.fn(),
+ state: {},
+});
+
+describe("source-search", () => {
+ describe("find", () => {
+ it("calls into CodeMirror APIs via clearSearch & doSearch", () => {
+ const ctx = { cm: getCM() };
+ expect(ctx.cm.state).toEqual({});
+ find(ctx, "test", false, modifiers);
+ // First we check the APIs called via clearSearch
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith(null);
+ // Next those via doSearch
+ expect(ctx.cm.operation).toHaveBeenCalled();
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith(null);
+ expect(ctx.cm.addOverlay).toHaveBeenCalledWith(
+ { token: expect.any(Function) },
+ { opaque: false }
+ );
+ expect(ctx.cm.getCursor).toHaveBeenCalledWith("anchor");
+ expect(ctx.cm.getCursor).toHaveBeenCalledWith("head");
+ const search = {
+ query: "test",
+ posTo: { line: 0, ch: 0 },
+ posFrom: { line: 0, ch: 0 },
+ overlay: { token: expect.any(Function) },
+ results: [],
+ };
+ expect(ctx.cm.state).toEqual({ search });
+ });
+
+ it("clears a previous overlay", () => {
+ const ctx = { cm: getCM() };
+ ctx.cm.state.search = {
+ query: "foo",
+ posTo: null,
+ posFrom: null,
+ overlay: { token: expect.any(Function) },
+ results: [],
+ };
+ find(ctx, "test", true, modifiers);
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith({
+ token: expect.any(Function),
+ });
+ });
+
+ it("clears for empty queries", () => {
+ const ctx = { cm: getCM() };
+ ctx.cm.state.search = {
+ query: "foo",
+ posTo: null,
+ posFrom: null,
+ overlay: null,
+ results: [],
+ };
+ find(ctx, "", true, modifiers);
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith(null);
+ ctx.cm.removeOverlay.mockClear();
+ ctx.cm.state.search.query = "bar";
+ find(ctx, "", true, modifiers);
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith(null);
+ });
+ });
+
+ describe("searchSourceForHighlight", () => {
+ it("calls into CodeMirror APIs and sets the correct selection", () => {
+ const line = 15;
+ const from = { line, ch: 1 };
+ const to = { line, ch: 5 };
+ const cm = {
+ ...getCM(),
+ setSelection: jest.fn(),
+ getSearchCursor: () => ({
+ find: () => true,
+ from: () => from,
+ to: () => to,
+ }),
+ };
+ const ed = { alignLine: jest.fn() };
+ const ctx = { cm, ed };
+
+ expect(ctx.cm.state).toEqual({});
+ searchSourceForHighlight(ctx, false, "test", false, modifiers, line, 1);
+
+ expect(ctx.cm.operation).toHaveBeenCalled();
+ expect(ctx.cm.removeOverlay).toHaveBeenCalledWith(null);
+ expect(ctx.cm.addOverlay).toHaveBeenCalledWith(
+ { token: expect.any(Function) },
+ { opaque: false }
+ );
+ expect(ctx.cm.getCursor).toHaveBeenCalledWith("anchor");
+ expect(ctx.cm.getCursor).toHaveBeenCalledWith("head");
+ expect(ed.alignLine).toHaveBeenCalledWith(line, "center");
+ expect(cm.setSelection).toHaveBeenCalledWith(from, to);
+ });
+ });
+
+ describe("findNext", () => {});
+
+ describe("findPrev", () => {});
+
+ describe("getMatchIndex", () => {
+ it("iterates in the matches", () => {
+ const count = 3;
+
+ // reverse 2, 1, 0, 2
+
+ let matchIndex = getMatchIndex(count, 2, true);
+ expect(matchIndex).toBe(1);
+
+ matchIndex = getMatchIndex(count, 1, true);
+ expect(matchIndex).toBe(0);
+
+ matchIndex = getMatchIndex(count, 0, true);
+ expect(matchIndex).toBe(2);
+
+ // forward 1, 2, 0, 1
+
+ matchIndex = getMatchIndex(count, 1, false);
+ expect(matchIndex).toBe(2);
+
+ matchIndex = getMatchIndex(count, 2, false);
+ expect(matchIndex).toBe(0);
+
+ matchIndex = getMatchIndex(count, 0, false);
+ expect(matchIndex).toBe(1);
+ });
+ });
+
+ describe("removeOverlay", () => {
+ it("calls CodeMirror APIs: removeOverlay, getCursor & setSelection", () => {
+ const ctx = {
+ cm: {
+ removeOverlay: jest.fn(),
+ getCursor,
+ state: {},
+ doc: {
+ setSelection: jest.fn(),
+ },
+ },
+ };
+ removeOverlay(ctx, "test");
+ expect(ctx.cm.removeOverlay).toHaveBeenCalled();
+ expect(ctx.cm.getCursor).toHaveBeenCalled();
+ expect(ctx.cm.doc.setSelection).toHaveBeenCalledWith(
+ { line: 90, ch: 54 },
+ { line: 90, ch: 54 },
+ { scroll: false }
+ );
+ });
+ });
+});