Files
fusion/packages/dashboard/app/utils/highlightDiff.test.ts
gsxdsm b826c2d7a9 feat(FN-683): add diff syntax highlighting with background colors
- Add highlightDiff utility React component for diff rendering
- Add CSS styles for diff-add (green), diff-del (red), diff-hunk (blue) line highlighting
- Integrate highlightDiff in TaskChangesTab for inline diffs
- Integrate highlightDiff in ChangedFilesModal for file diffs
- Add unit tests for highlightDiff utility
2026-04-02 09:58:04 -07:00

150 lines
5.2 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { highlightDiff } from "./highlightDiff";
import React from "react";
describe("highlightDiff", () => {
it("applies diff-add class to added lines starting with +", () => {
const result = highlightDiff("+hello world");
expect(result).toHaveLength(1);
const span = result[0] as React.ReactElement;
expect(span.type).toBe("span");
expect(span.props.className).toBe("diff-add");
expect(span.props.children).toBe("+hello world\n");
});
it("applies diff-del class to removed lines starting with -", () => {
const result = highlightDiff("-world");
expect(result).toHaveLength(1);
const span = result[0] as React.ReactElement;
expect(span.type).toBe("span");
expect(span.props.className).toBe("diff-del");
expect(span.props.children).toBe("-world\n");
});
it("applies diff-hunk class to hunk headers starting with @@", () => {
const result = highlightDiff("@@ -1,5 +1,6 @@ function");
expect(result).toHaveLength(1);
const span = result[0] as React.ReactElement;
expect(span.type).toBe("span");
expect(span.props.className).toBe("diff-hunk");
expect(span.props.children).toBe("@@ -1,5 +1,6 @@ function\n");
});
it("does not apply special class to context lines", () => {
const result = highlightDiff(" context line");
expect(result).toHaveLength(1);
// Context lines should be returned as plain text fragments
const fragment = result[0] as React.ReactElement;
expect(fragment.type).toBe(React.Fragment);
expect(fragment.props.children).toBe(" context line\n");
});
it("does not apply diff-add class to +++ lines", () => {
const result = highlightDiff("+++ b/file.ts");
expect(result).toHaveLength(1);
const fragment = result[0] as React.ReactElement;
expect(fragment.type).toBe(React.Fragment);
expect(fragment.props.children).toBe("+++ b/file.ts\n");
});
it("does not apply diff-del class to --- lines", () => {
const result = highlightDiff("--- a/file.ts");
expect(result).toHaveLength(1);
const fragment = result[0] as React.ReactElement;
expect(fragment.type).toBe(React.Fragment);
expect(fragment.props.children).toBe("--- a/file.ts\n");
});
it("renders multiple lines correctly with different classes", () => {
const diff = `diff --git a/file.ts b/file.ts
--- a/file.ts
+++ b/file.ts
@@ -1,3 +1,4 @@
context line
+added line
-deleted line
another context`;
const result = highlightDiff(diff);
expect(result).toHaveLength(8);
// Line 0: diff --git - plain fragment
expect((result[0] as React.ReactElement).type).toBe(React.Fragment);
// Line 1: --- a/file.ts - plain fragment (not diff-del)
expect((result[1] as React.ReactElement).type).toBe(React.Fragment);
// Line 2: +++ b/file.ts - plain fragment (not diff-add)
expect((result[2] as React.ReactElement).type).toBe(React.Fragment);
// Line 3: @@ hunk header - diff-hunk
const hunkLine = result[3] as React.ReactElement;
expect(hunkLine.type).toBe("span");
expect(hunkLine.props.className).toBe("diff-hunk");
// Line 4: context - plain fragment
expect((result[4] as React.ReactElement).type).toBe(React.Fragment);
// Line 5: +added - diff-add
const addedLine = result[5] as React.ReactElement;
expect(addedLine.type).toBe("span");
expect(addedLine.props.className).toBe("diff-add");
// Line 6: -deleted - diff-del
const deletedLine = result[6] as React.ReactElement;
expect(deletedLine.type).toBe("span");
expect(deletedLine.props.className).toBe("diff-del");
// Line 7: another context - plain fragment
expect((result[7] as React.ReactElement).type).toBe(React.Fragment);
});
it("renders empty diff without errors", () => {
const result = highlightDiff("");
expect(result).toHaveLength(1);
// Empty string becomes single element with empty line
const fragment = result[0] as React.ReactElement;
expect(fragment.type).toBe(React.Fragment);
expect(fragment.props.children).toBe("\n");
});
it("handles single line without newline", () => {
const result = highlightDiff("+single line");
expect(result).toHaveLength(1);
const span = result[0] as React.ReactElement;
expect(span.type).toBe("span");
expect(span.props.className).toBe("diff-add");
expect(span.props.children).toBe("+single line\n");
});
it("handles diff header lines correctly", () => {
const diff = `diff --git a/src/index.ts b/src/index.ts
index 1234567..abcdefg 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -10,6 +10,7 @@ export`;
const result = highlightDiff(diff);
// 5 lines total (split by \n)
expect(result).toHaveLength(5);
// All header lines should be plain fragments, not diff-add/diff-del
expect((result[0] as React.ReactElement).type).toBe(React.Fragment);
expect((result[1] as React.ReactElement).type).toBe(React.Fragment);
expect((result[2] as React.ReactElement).type).toBe(React.Fragment); // --- a/src/index.ts
expect((result[3] as React.ReactElement).type).toBe(React.Fragment); // +++ b/src/index.ts
expect((result[4] as React.ReactElement).type).toBe("span");
expect((result[4] as React.ReactElement).props.className).toBe("diff-hunk");
});
});