diff --git a/src/tests/utils/markdownEditorUtils.test.ts b/src/tests/utils/markdownEditorUtils.test.ts new file mode 100644 index 00000000..e794a2f6 --- /dev/null +++ b/src/tests/utils/markdownEditorUtils.test.ts @@ -0,0 +1,202 @@ +import { + parseLine, + detectPrefix, + allLinesHavePrefix, + createLineEditRange, +} from "../../utils/markdownEditorUtils"; + +describe("markdownEditorUtils", () => { + describe("parseLine", () => { + it("should return empty whitespace for line with no leading spaces", () => { + const result = parseLine("Hello World"); + expect(result).toEqual({ + leadingWhitespace: "", + content: "Hello World", + }); + }); + + it("should extract leading spaces", () => { + const result = parseLine(" Hello"); + expect(result).toEqual({ + leadingWhitespace: " ", + content: "Hello", + }); + }); + + it("should extract leading tabs", () => { + const result = parseLine("\t\tHello"); + expect(result).toEqual({ + leadingWhitespace: "\t\t", + content: "Hello", + }); + }); + + it("should handle mixed whitespace", () => { + const result = parseLine(" \t Hello"); + expect(result).toEqual({ + leadingWhitespace: " \t ", + content: "Hello", + }); + }); + + it("should handle empty line", () => { + const result = parseLine(""); + expect(result).toEqual({ + leadingWhitespace: "", + content: "", + }); + }); + + it("should handle line with only whitespace", () => { + const result = parseLine(" "); + expect(result).toEqual({ + leadingWhitespace: " ", + content: "", + }); + }); + }); + + describe("detectPrefix", () => { + it("should return undefined matched when no prefix found", () => { + const result = detectPrefix("Hello World", ["#", "##", "###"]); + expect(result).toEqual({ + matched: undefined, + stripped: "Hello World", + }); + }); + + it("should detect single hash heading prefix", () => { + const result = detectPrefix("# Hello", ["#", "##", "###"]); + expect(result).toEqual({ + matched: "#", + stripped: "Hello", + }); + }); + + it("should detect double hash heading prefix", () => { + const result = detectPrefix("## Hello", ["#", "##", "###"]); + expect(result).toEqual({ + matched: "##", + stripped: "Hello", + }); + }); + + it("should detect triple hash heading prefix", () => { + const result = detectPrefix("### Hello", ["#", "##", "###"]); + expect(result).toEqual({ + matched: "###", + stripped: "Hello", + }); + }); + + it("should detect unordered list prefix", () => { + const result = detectPrefix("- Item", ["-"]); + expect(result).toEqual({ + matched: "-", + stripped: "Item", + }); + }); + + it("should handle prefix with no content after it", () => { + const result = detectPrefix("#", ["#", "##"]); + expect(result).toEqual({ + matched: "#", + stripped: "", + }); + }); + + it("should not match prefix that is not in the list", () => { + const result = detectPrefix("* Item", ["-"]); + expect(result).toEqual({ + matched: undefined, + stripped: "* Item", + }); + }); + + it("should handle content with leading spaces before prefix", () => { + const result = detectPrefix(" # Hello", ["#", "##"]); + expect(result).toEqual({ + matched: "#", + stripped: "Hello", + }); + }); + + it("should not match when prefix is part of word", () => { + const result = detectPrefix("#hashtag", ["#"]); + expect(result).toEqual({ + matched: undefined, + stripped: "#hashtag", + }); + }); + }); + + describe("allLinesHavePrefix", () => { + it("should return true when all lines have the same prefix", () => { + const lines = ["# Hello", "# World", "# Test"]; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(true); + }); + + it("should return false when lines have different prefixes", () => { + const lines = ["# Hello", "## World", "# Test"]; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(false); + }); + + it("should return false when some lines have no prefix", () => { + const lines = ["# Hello", "World", "# Test"]; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(false); + }); + + it("should handle single line", () => { + const lines = ["# Hello"]; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(true); + }); + + it("should handle lines with leading whitespace", () => { + const lines = [" # Hello", " # World"]; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(true); + }); + + it("should return true for empty array", () => { + const lines: string[] = []; + const result = allLinesHavePrefix(lines, "#", ["#", "##", "###"]); + expect(result).toBe(true); + }); + }); + + describe("createLineEditRange", () => { + it("should create correct range for line 1", () => { + const result = createLineEditRange(1, 10); + expect(result).toEqual({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 11, + }); + }); + + it("should create correct range for arbitrary line", () => { + const result = createLineEditRange(5, 20); + expect(result).toEqual({ + startLineNumber: 5, + startColumn: 1, + endLineNumber: 5, + endColumn: 21, + }); + }); + + it("should handle zero-length line", () => { + const result = createLineEditRange(3, 0); + expect(result).toEqual({ + startLineNumber: 3, + startColumn: 1, + endLineNumber: 3, + endColumn: 1, + }); + }); + }); +}); diff --git a/src/utils/testing/__mocks__/monaco-editor.ts b/src/utils/testing/__mocks__/monaco-editor.ts new file mode 100644 index 00000000..fe513ce1 --- /dev/null +++ b/src/utils/testing/__mocks__/monaco-editor.ts @@ -0,0 +1,22 @@ +/** + * Mock for monaco-editor module used in tests + * This allows testing utilities that import Monaco types without loading the full editor + */ + +export class Selection { + constructor( + public startLineNumber: number, + public startColumn: number, + public endLineNumber: number, + public endColumn: number + ) {} +} + +export const editor = {}; + +export interface IRange { + startLineNumber: number; + startColumn: number; + endLineNumber: number; + endColumn: number; +} diff --git a/vite.config.ts b/vite.config.ts index 873b9d67..1c1a8feb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,4 @@ +import { fileURLToPath } from "node:url"; import { defineConfig as defineViteConfig, mergeConfig } from "vite"; import { defineConfig as defineVitestConfig, configDefaults } from "vitest/config"; import react from "@vitejs/plugin-react"; @@ -23,6 +24,12 @@ const vitestConfig = defineVitestConfig({ environment: "jsdom", setupFiles: "./src/utils/testing/setup.ts", exclude: [...configDefaults.exclude, "**/e2e/**"], + deps: { + inline: ["monaco-editor"], + }, + alias: { + "monaco-editor": fileURLToPath(new URL("./src/utils/testing/__mocks__/monaco-editor.ts", import.meta.url)), + }, }, });