Initial commit
This commit is contained in:
580
test/data-formatter.test.ts
Normal file
580
test/data-formatter.test.ts
Normal file
@@ -0,0 +1,580 @@
|
||||
import { expect, test, describe } from "bun:test";
|
||||
import {
|
||||
formatContext,
|
||||
formatBody,
|
||||
formatComments,
|
||||
formatReviewComments,
|
||||
formatChangedFiles,
|
||||
formatChangedFilesWithSHA,
|
||||
} from "../src/github/data/formatter";
|
||||
import type {
|
||||
GitHubPullRequest,
|
||||
GitHubIssue,
|
||||
GitHubComment,
|
||||
GitHubFile,
|
||||
} from "../src/github/types";
|
||||
import type { GitHubFileWithSHA } from "../src/github/data/fetcher";
|
||||
|
||||
describe("formatContext", () => {
|
||||
test("formats PR context correctly", () => {
|
||||
const prData: GitHubPullRequest = {
|
||||
title: "Test PR",
|
||||
body: "PR body",
|
||||
author: { login: "test-user" },
|
||||
baseRefName: "main",
|
||||
headRefName: "feature/test",
|
||||
headRefOid: "abc123",
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
additions: 50,
|
||||
deletions: 30,
|
||||
state: "OPEN",
|
||||
commits: {
|
||||
totalCount: 3,
|
||||
nodes: [],
|
||||
},
|
||||
files: {
|
||||
nodes: [{} as GitHubFile, {} as GitHubFile],
|
||||
},
|
||||
comments: {
|
||||
nodes: [],
|
||||
},
|
||||
reviews: {
|
||||
nodes: [],
|
||||
},
|
||||
};
|
||||
|
||||
const result = formatContext(prData, true);
|
||||
expect(result).toBe(
|
||||
`PR Title: Test PR
|
||||
PR Author: test-user
|
||||
PR Branch: feature/test -> main
|
||||
PR State: OPEN
|
||||
PR Additions: 50
|
||||
PR Deletions: 30
|
||||
Total Commits: 3
|
||||
Changed Files: 2 files`,
|
||||
);
|
||||
});
|
||||
|
||||
test("formats Issue context correctly", () => {
|
||||
const issueData: GitHubIssue = {
|
||||
title: "Test Issue",
|
||||
body: "Issue body",
|
||||
author: { login: "test-user" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
state: "OPEN",
|
||||
comments: {
|
||||
nodes: [],
|
||||
},
|
||||
};
|
||||
|
||||
const result = formatContext(issueData, false);
|
||||
expect(result).toBe(
|
||||
`Issue Title: Test Issue
|
||||
Issue Author: test-user
|
||||
Issue State: OPEN`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatBody", () => {
|
||||
test("replaces image URLs with local paths", () => {
|
||||
const body = `Here is some text with an image: 
|
||||
|
||||
And another one: 
|
||||
|
||||
Some more text.`;
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/test-image.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
[
|
||||
"https://github.com/user-attachments/assets/another-image.jpg",
|
||||
"/tmp/github-images/image-1234-1.jpg",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatBody(body, imageUrlMap);
|
||||
expect(result)
|
||||
.toBe(`Here is some text with an image: 
|
||||
|
||||
And another one: 
|
||||
|
||||
Some more text.`);
|
||||
});
|
||||
|
||||
test("handles empty image map", () => {
|
||||
const body = "No images here";
|
||||
const imageUrlMap = new Map<string, string>();
|
||||
|
||||
const result = formatBody(body, imageUrlMap);
|
||||
expect(result).toBe("No images here");
|
||||
});
|
||||
|
||||
test("preserves body when no images match", () => {
|
||||
const body = "";
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/different.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatBody(body, imageUrlMap);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("handles multiple occurrences of same image", () => {
|
||||
const body = `First: 
|
||||
Second: `;
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/test.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatBody(body, imageUrlMap);
|
||||
expect(result).toBe(`First: 
|
||||
Second: `);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatComments", () => {
|
||||
test("formats comments correctly", () => {
|
||||
const comments: GitHubComment[] = [
|
||||
{
|
||||
id: "1",
|
||||
databaseId: "100001",
|
||||
body: "First comment",
|
||||
author: { login: "user1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
databaseId: "100002",
|
||||
body: "Second comment",
|
||||
author: { login: "user2" },
|
||||
createdAt: "2023-01-02T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
const result = formatComments(comments);
|
||||
expect(result).toBe(
|
||||
`[user1 at 2023-01-01T00:00:00Z]: First comment\n\n[user2 at 2023-01-02T00:00:00Z]: Second comment`,
|
||||
);
|
||||
});
|
||||
|
||||
test("returns empty string for empty comments array", () => {
|
||||
const result = formatComments([]);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("replaces image URLs in comments", () => {
|
||||
const comments: GitHubComment[] = [
|
||||
{
|
||||
id: "1",
|
||||
databaseId: "100001",
|
||||
body: "Check out this screenshot: ",
|
||||
author: { login: "user1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
databaseId: "100002",
|
||||
body: "Here's another image: ",
|
||||
author: { login: "user2" },
|
||||
createdAt: "2023-01-02T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/screenshot.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
[
|
||||
"https://github.com/user-attachments/assets/bug-report.jpg",
|
||||
"/tmp/github-images/image-1234-1.jpg",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatComments(comments, imageUrlMap);
|
||||
expect(result).toBe(
|
||||
`[user1 at 2023-01-01T00:00:00Z]: Check out this screenshot: \n\n[user2 at 2023-01-02T00:00:00Z]: Here's another image: `,
|
||||
);
|
||||
});
|
||||
|
||||
test("handles comments with multiple images", () => {
|
||||
const comments: GitHubComment[] = [
|
||||
{
|
||||
id: "1",
|
||||
databaseId: "100001",
|
||||
body: "Two images:  and ",
|
||||
author: { login: "user1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/first.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
[
|
||||
"https://github.com/user-attachments/assets/second.png",
|
||||
"/tmp/github-images/image-1234-1.png",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatComments(comments, imageUrlMap);
|
||||
expect(result).toBe(
|
||||
`[user1 at 2023-01-01T00:00:00Z]: Two images:  and `,
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves comments when imageUrlMap is undefined", () => {
|
||||
const comments: GitHubComment[] = [
|
||||
{
|
||||
id: "1",
|
||||
databaseId: "100001",
|
||||
body: "Image: ",
|
||||
author: { login: "user1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
const result = formatComments(comments);
|
||||
expect(result).toBe(
|
||||
`[user1 at 2023-01-01T00:00:00Z]: Image: `,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatReviewComments", () => {
|
||||
test("formats review with body and comments correctly", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300001",
|
||||
author: { login: "reviewer1" },
|
||||
body: "This is a great PR! LGTM.",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [
|
||||
{
|
||||
id: "comment1",
|
||||
databaseId: "200001",
|
||||
body: "Nice implementation",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/index.ts",
|
||||
line: 42,
|
||||
},
|
||||
{
|
||||
id: "comment2",
|
||||
databaseId: "200002",
|
||||
body: "Consider adding error handling",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/utils.ts",
|
||||
line: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = formatReviewComments(reviewData);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED\n [Comment on src/index.ts:42]: Nice implementation\n [Comment on src/utils.ts:?]: Consider adding error handling`,
|
||||
);
|
||||
});
|
||||
|
||||
test("formats review with only body (no comments) correctly", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300002",
|
||||
author: { login: "reviewer1" },
|
||||
body: "Looks good to me!",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = formatReviewComments(reviewData);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED`,
|
||||
);
|
||||
});
|
||||
|
||||
test("formats review without body correctly", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300003",
|
||||
author: { login: "reviewer1" },
|
||||
body: "",
|
||||
state: "COMMENTED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [
|
||||
{
|
||||
id: "comment1",
|
||||
databaseId: "200003",
|
||||
body: "Small suggestion here",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/main.ts",
|
||||
line: 15,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = formatReviewComments(reviewData);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: COMMENTED\n [Comment on src/main.ts:15]: Small suggestion here`,
|
||||
);
|
||||
});
|
||||
|
||||
test("formats multiple reviews correctly", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300004",
|
||||
author: { login: "reviewer1" },
|
||||
body: "Needs changes",
|
||||
state: "CHANGES_REQUESTED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "review2",
|
||||
databaseId: "300005",
|
||||
author: { login: "reviewer2" },
|
||||
body: "LGTM",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-02T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = formatReviewComments(reviewData);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: CHANGES_REQUESTED\n\n[Review by reviewer2 at 2023-01-02T00:00:00Z]: APPROVED`,
|
||||
);
|
||||
});
|
||||
|
||||
test("returns empty string for null reviewData", () => {
|
||||
const result = formatReviewComments(null);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("returns empty string for empty reviewData", () => {
|
||||
const result = formatReviewComments({ nodes: [] });
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("replaces image URLs in review comments", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300001",
|
||||
author: { login: "reviewer1" },
|
||||
body: "Review with image: ",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [
|
||||
{
|
||||
id: "comment1",
|
||||
databaseId: "200001",
|
||||
body: "Comment with image: ",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/index.ts",
|
||||
line: 42,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/review.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
[
|
||||
"https://github.com/user-attachments/assets/comment.png",
|
||||
"/tmp/github-images/image-1234-1.png",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatReviewComments(reviewData, imageUrlMap);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED\n [Comment on src/index.ts:42]: Comment with image: `,
|
||||
);
|
||||
});
|
||||
|
||||
test("handles multiple images in review comments", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300001",
|
||||
author: { login: "reviewer1" },
|
||||
body: "Good work",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [
|
||||
{
|
||||
id: "comment1",
|
||||
databaseId: "200001",
|
||||
body: "Two issues:  and ",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/main.ts",
|
||||
line: 15,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const imageUrlMap = new Map([
|
||||
[
|
||||
"https://github.com/user-attachments/assets/issue1.png",
|
||||
"/tmp/github-images/image-1234-0.png",
|
||||
],
|
||||
[
|
||||
"https://github.com/user-attachments/assets/issue2.png",
|
||||
"/tmp/github-images/image-1234-1.png",
|
||||
],
|
||||
]);
|
||||
|
||||
const result = formatReviewComments(reviewData, imageUrlMap);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED\n [Comment on src/main.ts:15]: Two issues:  and `,
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves review comments when imageUrlMap is undefined", () => {
|
||||
const reviewData = {
|
||||
nodes: [
|
||||
{
|
||||
id: "review1",
|
||||
databaseId: "300001",
|
||||
author: { login: "reviewer1" },
|
||||
body: "Review body",
|
||||
state: "APPROVED",
|
||||
submittedAt: "2023-01-01T00:00:00Z",
|
||||
comments: {
|
||||
nodes: [
|
||||
{
|
||||
id: "comment1",
|
||||
databaseId: "200001",
|
||||
body: "Image: ",
|
||||
author: { login: "reviewer1" },
|
||||
createdAt: "2023-01-01T00:00:00Z",
|
||||
path: "src/index.ts",
|
||||
line: 42,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = formatReviewComments(reviewData);
|
||||
expect(result).toBe(
|
||||
`[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED\n [Comment on src/index.ts:42]: Image: `,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatChangedFiles", () => {
|
||||
test("formats changed files correctly", () => {
|
||||
const files: GitHubFile[] = [
|
||||
{
|
||||
path: "src/index.ts",
|
||||
additions: 10,
|
||||
deletions: 5,
|
||||
changeType: "MODIFIED",
|
||||
},
|
||||
{
|
||||
path: "src/utils.ts",
|
||||
additions: 20,
|
||||
deletions: 0,
|
||||
changeType: "ADDED",
|
||||
},
|
||||
];
|
||||
|
||||
const result = formatChangedFiles(files);
|
||||
expect(result).toBe(
|
||||
`- src/index.ts (MODIFIED) +10/-5\n- src/utils.ts (ADDED) +20/-0`,
|
||||
);
|
||||
});
|
||||
|
||||
test("returns empty string for empty files array", () => {
|
||||
const result = formatChangedFiles([]);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatChangedFilesWithSHA", () => {
|
||||
test("formats changed files with SHA correctly", () => {
|
||||
const files: GitHubFileWithSHA[] = [
|
||||
{
|
||||
path: "src/index.ts",
|
||||
additions: 10,
|
||||
deletions: 5,
|
||||
changeType: "MODIFIED",
|
||||
sha: "abc123",
|
||||
},
|
||||
{
|
||||
path: "src/utils.ts",
|
||||
additions: 20,
|
||||
deletions: 0,
|
||||
changeType: "ADDED",
|
||||
sha: "def456",
|
||||
},
|
||||
];
|
||||
|
||||
const result = formatChangedFilesWithSHA(files);
|
||||
expect(result).toBe(
|
||||
`- src/index.ts (MODIFIED) +10/-5 SHA: abc123\n- src/utils.ts (ADDED) +20/-0 SHA: def456`,
|
||||
);
|
||||
});
|
||||
|
||||
test("returns empty string for empty files array", () => {
|
||||
const result = formatChangedFilesWithSHA([]);
|
||||
expect(result).toBe("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user