Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Test build"
name: "Test and Build"

on:
push:
Expand All @@ -18,6 +18,27 @@ env:
NODE_VERSION: 18

jobs:
test:
name: Run tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive

- name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
cache: yarn
node-version: ${{ env.NODE_VERSION }}

- name: Install dependencies
run: script/bootstrap

- name: Run tests
run: npx vitest run

build_frontend:
name: Test build
runs-on: ubuntu-latest
Expand Down
30 changes: 24 additions & 6 deletions src/tools/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,30 @@ export const markdownWithRepositoryContext = (input: string, repository?: Reposi
return x.replace("(#", `(/hacs/repository/${repository.id}#`);
});

// Add references to issues and PRs
input = input.replace(/(?:\w[\w-.]+\/\w[\w-.]+|\B)#[1-9]\d*\b/g, (reference) => {
const fullReference = reference.replace(/^#/, `${repository.full_name}#`);
const [fullName, issue] = fullReference.split("#");
return `[${reference}](https://github.com/${fullName}/issues/${issue})`;
});
// Add references to issues and PRs (avoid CSS hex colors and code blocks)
input = input.replace(
/(^|[\s])((?:\w[\w-.]+\/\w[\w-.]+)?#[1-9]\d{0,4})\b/g,
(match, prefix, reference) => {
const issueNumber = reference.split("#")[1];

// Skip if it's a valid CSS hex color (only contains 0-9a-f and is 3 or 6 digits)
if (issueNumber && /^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(issueNumber)) {
// Check if it's in a CSS context
const beforeMatch = input.substring(0, input.indexOf(match));
if (/(?:color|background|border):\s*$/.test(beforeMatch) || /:\s*$/.test(beforeMatch)) {
return match;
}
}

const fullReference = reference.includes("/")
? reference
: `${repository.full_name}${reference}`;
const [fullName, issue] = fullReference.split("#");
return fullName && issue
? `${prefix}[${reference}](https://github.com/${fullName}/issues/${issue})`
: match;
},
);
}
return input;
};
119 changes: 119 additions & 0 deletions tests/markdown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { describe, it, expect } from "vitest";
import { markdownWithRepositoryContext } from "../src/tools/markdown.js";
import type { RepositoryInfo } from "../src/data/repository.js";

const mockRepository: RepositoryInfo = {
id: "123",
full_name: "awesome/repo",
default_branch: "main",
available_version: "v1.0.0",
} as RepositoryInfo;

describe("markdownWithRepositoryContext", () => {
it("should convert GitHub issue references to links", () => {
const input = "See issue #123 for details";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe("See issue [#123](https://github.com/awesome/repo/issues/123) for details");
});

it("should NOT convert CSS color codes", () => {
const input = "Set color: #123456 in your CSS";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe("Set color: #123456 in your CSS");
});

it("should NOT convert CSS shorthand colors", () => {
const input = "Use background:#123; border:#abc;";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe("Use background:#123; border:#abc;");
});

it("should handle mixed scenarios correctly", () => {
const input = "Fix issue #42 but keep color:#333 and border:#456 styles intact";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"Fix issue [#42](https://github.com/awesome/repo/issues/42) but keep color:#333 and border:#456 styles intact",
);
});

it("should convert legitimate issue numbers but not hex colors", () => {
const input = "Issue #123 was fixed, but color: #456789 remains unchanged";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"Issue [#123](https://github.com/awesome/repo/issues/123) was fixed, but color: #456789 remains unchanged",
);
});

it("should work without repository context", () => {
const input = "Some text with #123 reference";
const result = markdownWithRepositoryContext(input);
expect(result).toBe("Some text with #123 reference");
});

it("should NOT convert CSS hex colors in code examples", () => {
const input = "Example CSS: div { color:#ff0000; background:#123456; }";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe("Example CSS: div { color:#ff0000; background:#123456; }");
});

it("should handle repository/issue references correctly", () => {
const input = "Check out awesome/other-repo#456 for more info";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"Check out [awesome/other-repo#456](https://github.com/awesome/other-repo/issues/456) for more info",
);
});

it("should handle issue numbers with 8 and 9 (non-hex digits)", () => {
const input = "See issues #789 and #98 for details";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"See issues [#789](https://github.com/awesome/repo/issues/789) and [#98](https://github.com/awesome/repo/issues/98) for details",
);
});

it("should NOT convert 6-digit hex colors in CSS context", () => {
const input = "Apply color:#ffffff and background-color:#000000 styles";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe("Apply color:#ffffff and background-color:#000000 styles");
});

it("should convert valid issue numbers that contain non-hex digits", () => {
const input = "Fixed in #987 and resolved in #1289";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"Fixed in [#987](https://github.com/awesome/repo/issues/987) and resolved in [#1289](https://github.com/awesome/repo/issues/1289)",
);
});

it("should handle mixed CSS and issue references in same text", () => {
const input = "Issue #789 fixed. Use border:#123; and see #890 too";
const result = markdownWithRepositoryContext(input, mockRepository);
expect(result).toBe(
"Issue [#789](https://github.com/awesome/repo/issues/789) fixed. Use border:#123; and see [#890](https://github.com/awesome/repo/issues/890) too",
);
});
it("should handle large text efficiently", () => {
// Create a large string with mixed content to test performance
const parts: string[] = [];
for (let i = 0; i < 25_000; i++) {
parts.push(`Issue #${i + 1} was fixed. Use color:#${String(i).padStart(3, "0")}; in CSS.`);
}
const input = parts.join(" ");

const start = performance.now();
const result = markdownWithRepositoryContext(input, mockRepository);
const end = performance.now();

// Should complete in reasonable time (< 100ms for this test)
expect(end - start).toBeLessThan(100);

// Should have converted all issue references
expect(result).toContain("[#1](https://github.com/awesome/repo/issues/1)");
expect(result).toContain("[#25000](https://github.com/awesome/repo/issues/25000)");

// Should not have converted CSS colors
expect(result).toContain("color:#001;");
expect(result).toContain("color:#099;");
});
});
8 changes: 8 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
environment: "node",
include: ["tests/**/*.test.{js,ts}"],
},
});
Loading