diff --git a/code-review-management/app/api/v1/[owner]/[repo]/commit/[ref]/diff/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/commit/[ref]/diff/route.test.ts new file mode 100644 index 000000000..43c297a21 --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/commit/[ref]/diff/route.test.ts @@ -0,0 +1,181 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/commit/{ref}/diff +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; + +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +jest.mock("octokit", () => ({ + RequestError: jest.fn(), + Octokit: jest.fn(), +})); + +interface MockOctokitInstance { + rest: { + repos: { + getCommit: jest.Mock; + }; + }; +} + +describe("GET /api/v1/{owner}/{repo}/commit/{ref}/diff", () => { + const mockContext = { owner: "mock-owner", repo: "mock-repo", ref: "abc123" }; + const mockOctokitInstance: MockOctokitInstance = { + rest: { repos: { getCommit: jest.fn() } }, + }; + let mockRequest: Request; + + beforeEach(() => { + jest.clearAllMocks(); + mockRequest = new Request("http://localhost:3000/api/v1/mock-owner/mock-repo/commit/abc123/diff"); + jest.mocked(Octokit).mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with commit diff when authenticated", async () => { + mockOctokitInstance.rest.repos.getCommit.mockResolvedValue({ + data: "diff content", + }); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect(mockOctokitInstance.rest.repos.getCommit).toHaveBeenCalledWith({ + owner: "mock-owner", + repo: "mock-repo", + ref: "abc123", + mediaType: { format: "diff" }, + }); + + const data: unknown = await response.json(); + expect(typeof data).toBe("string"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.repos.getCommit.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/diff/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/diff/route.test.ts new file mode 100644 index 000000000..8377a356e --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/diff/route.test.ts @@ -0,0 +1,238 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/commit/compare/diff +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultUser } from "@/mocks/tests/users"; + +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +jest.mock("octokit", () => ({ + RequestError: jest.fn(), + Octokit: jest.fn(), +})); + +interface MockOctokitInstance { + rest: { + repos: { + compareCommitsWithBasehead: jest.Mock; + }; + }; +} + +describe("GET /api/v1/{owner}/{repo}/commit/compare/diff", () => { + const mockCompare = { + base_commit: { + url: "", + sha: "base-sha", + html_url: "", + commit: { message: "", author: { date: "", email: "", name: "" }, committer: { date: "", email: "", name: "" } }, + author: getDefaultUser(), + committer: getDefaultUser(), + }, + merge_base_commit: { + url: "", + sha: "merge-base", + html_url: "", + commit: { message: "", author: { date: "", email: "", name: "" }, committer: { date: "", email: "", name: "" } }, + author: getDefaultUser(), + committer: getDefaultUser(), + }, + html_url: "", + status: "ahead", + ahead_by: 1, + behind_by: 0, + total_commits: 1, + files: [], + }; + const mockOctokitInstance: MockOctokitInstance = { + rest: { repos: { compareCommitsWithBasehead: jest.fn() } }, + }; + const mockRequest = new Request( + "http://localhost:3000/api/v1/mock-owner/mock-repo/commit/compare/diff?base=main&head=feature", + ); + const mockContext = { owner: "mock-owner", repo: "mock-repo" }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.mocked(Octokit).mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with compare diff when authenticated", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValueOnce({ + data: mockCompare, + }); + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValueOnce({ + data: "diff data", + }); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect(mockOctokitInstance.rest.repos.compareCommitsWithBasehead).toHaveBeenCalledTimes(2); + expect(mockOctokitInstance.rest.repos.compareCommitsWithBasehead).toHaveBeenNthCalledWith( + 1, + { + owner: "mock-owner", + repo: "mock-repo", + basehead: "main...feature", + }, + ); + expect(mockOctokitInstance.rest.repos.compareCommitsWithBasehead).toHaveBeenNthCalledWith( + 2, + { + owner: "mock-owner", + repo: "mock-repo", + basehead: "merge-base...feature", + mediaType: { + format: "diff", + }, + }, + ); + + const data: unknown = await response.json(); + expect(typeof data).toBe("string"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValueOnce({ + data: { invalid: "payload" }, + }); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/route.test.ts new file mode 100644 index 000000000..44ea0b934 --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/commit/compare/route.test.ts @@ -0,0 +1,281 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/commit/compare +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultUser } from "@/mocks/tests/users"; + +// Mock next-auth/jwt +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +// Mock octokit +jest.mock("octokit", () => ({ + RequestError: jest.fn(), // Added this to avoid undefined error + Octokit: jest.fn(), // Mocked in the beforeEach() +})); + +// Define types for our mocks +interface MockOctokitInstance { + rest: { + repos: { + compareCommitsWithBasehead: jest.Mock; + }; + }; +} + +describe("GET /api/v1/{owner}/{repo}/commit/compare", () => { + const mockCompare = { + base_commit: { + url: "", + sha: "base-sha", + html_url: "", + commit: { + message: "", + author: { date: "", email: "", name: "" }, + committer: { date: "", email: "", name: "" }, + }, + author: getDefaultUser(), + committer: getDefaultUser(), + }, + merge_base_commit: { + url: "", + sha: "merge-base", + html_url: "", + commit: { + message: "", + author: { date: "", email: "", name: "" }, + committer: { date: "", email: "", name: "" }, + }, + author: getDefaultUser(), + committer: getDefaultUser(), + }, + html_url: "", + status: "ahead", + ahead_by: 1, + behind_by: 0, + total_commits: 1, + files: [], + }; + + const mockOctokitInstance: MockOctokitInstance = { + rest: { + repos: { + compareCommitsWithBasehead: jest.fn(), + }, + }, + }; + + const mockContext = { + owner: "mock-owner", + repo: "mock-repo", + }; + + let mockRequest: Request; + + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Create a mock request + mockRequest = new Request( + "http://localhost:3000/api/v1/mock-owner/mock-repo/commit/compare?base=main&head=feature", + ); + + jest + .mocked(Octokit) + .mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + // Mock valid token + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, // 1 hour from now + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with compare commits when authenticated", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValue( + { + data: mockCompare, + }, + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect( + mockOctokitInstance.rest.repos.compareCommitsWithBasehead, + ).toHaveBeenCalledTimes(2); + expect( + mockOctokitInstance.rest.repos.compareCommitsWithBasehead, + ).toHaveBeenNthCalledWith(1, { + owner: "mock-owner", + repo: "mock-repo", + basehead: "main...feature", + }); + expect( + mockOctokitInstance.rest.repos.compareCommitsWithBasehead, + ).toHaveBeenNthCalledWith(2, { + owner: "mock-owner", + repo: "mock-repo", + basehead: "merge-base...feature", + }); + + const data: unknown = await response.json(); + expect(typeof data).toBe("object"); + }); + + it("should filter compare commits using CompareCommitsSchema", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValue( + { + data: { ...mockCompare, extraField: "blah" }, + }, + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + const data = (await response.json()) as Record; + + expect(data).not.toHaveProperty("extraField"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockResolvedValue( + { + data: { invalid: "payload" }, + }, + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.repos.compareCommitsWithBasehead.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest, { + params: Promise.resolve(mockContext), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/[owner]/[repo]/issues/[issue_number]/comment/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/issues/[issue_number]/comment/route.test.ts new file mode 100644 index 000000000..cf79820d4 --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/issues/[issue_number]/comment/route.test.ts @@ -0,0 +1,328 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/issues/{issue_number}/comment +*/ + +import { POST } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultUser } from "@/mocks/tests/users"; + +// Mock next-auth/jwt +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +// Mock octokit +jest.mock("octokit", () => ({ + RequestError: jest.fn(), // Added this to avoid undefined error + Octokit: jest.fn(), // Mocked in the beforeEach() +})); + +// Define types for our mocks +interface MockOctokitInstance { + rest: { + issues: { + createComment: jest.Mock; + }; + }; +} + +describe("POST /api/v1/{owner}/{repo}/issues/{issue_number}/comment", () => { + const mockIssueComment = { + id: 0, + body: "test comment", + user: getDefaultUser(), + created_at: "", + updated_at: "", + author_association: "CONTRIBUTOR", + extraField: "blah", + }; + + const mockIssueCommentWithoutExtraField = { + id: 0, + body: "test comment", + user: getDefaultUser(), + created_at: "", + updated_at: "", + author_association: "CONTRIBUTOR", + }; + + const mockRequestBody = { + body: "test comment", + }; + + const mockContext = { + owner: "mock-owner", + repo: "mock-repo", + issue_number: "123", + }; + + const mockOctokitInstance: MockOctokitInstance = { + rest: { + issues: { + createComment: jest.fn(), + }, + }, + }; + + let mockRequest: Request; + + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Create a mock request + mockRequest = new Request( + "http://localhost:3000/api/v1/mock-owner/mock-repo/issues/123/comment", + { + method: "POST", + body: JSON.stringify(mockRequestBody), + headers: { + "Content-Type": "application/json", + }, + }, + ); + + jest + .mocked(Octokit) + .mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + // Mock valid token + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, // 1 hour from now + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with comment when authenticated", async () => { + mockOctokitInstance.rest.issues.createComment.mockResolvedValue({ + data: mockIssueCommentWithoutExtraField, + }); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect( + mockOctokitInstance.rest.issues.createComment, + ).toHaveBeenCalledWith({ + owner: mockContext.owner, + repo: mockContext.repo, + issue_number: Number(mockContext.issue_number), + body: mockRequestBody.body, + }); + + const data: unknown = await response.json(); + expect(data).not.toBeNull(); + expect(typeof data).toBe("object"); + }); + + it("should filter comment using IssueCommentSchema", async () => { + mockOctokitInstance.rest.issues.createComment.mockResolvedValue({ + data: mockIssueComment, + }); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + const data = (await response.json()) as Record; + expect(data).not.toHaveProperty("extraField"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + const mockInvalidIssueComment = { + // Invalid data that will fail IssueCommentSchema.parse + id: "invalid-id", // Should be number + user: getDefaultUser(), + created_at: "", + updated_at: "", + author_association: "CONTRIBUTOR", + }; + + mockOctokitInstance.rest.issues.createComment.mockResolvedValue({ + data: mockInvalidIssueComment, + }); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.issues.createComment.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await POST(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + issue_number: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); + diff --git a/code-review-management/app/api/v1/[owner]/[repo]/permission/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/permission/route.test.ts new file mode 100644 index 000000000..f32208cc5 --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/permission/route.test.ts @@ -0,0 +1,302 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/permission +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultUser } from "@/mocks/tests/users"; + +// Mock next-auth/jwt +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +// Mock octokit +jest.mock("octokit", () => ({ + RequestError: jest.fn(), // Added this to avoid undefined error + Octokit: jest.fn(), // Mocked in the beforeEach() +})); + +// Define types for our mocks +interface MockOctokitInstance { + rest: { + repos: { + getCollaboratorPermissionLevel: jest.Mock; + }; + }; +} + +describe("GET /api/v1/{owner}/{repo}/permission", () => { + const mockPermission = { + permission: "admin", + role_name: "admin", + user: getDefaultUser(), + extraField: "blah", + }; + + const mockPermissionWithoutExtraField = { + permission: "admin", + role_name: "admin", + user: getDefaultUser(), + }; + + const mockContext = { + owner: "mock-owner", + repo: "mock-repo", + }; + + const mockOctokitInstance: MockOctokitInstance = { + rest: { + repos: { + getCollaboratorPermissionLevel: jest.fn(), + }, + }, + }; + + let mockRequest: Request; + + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Create a mock request + mockRequest = new Request( + "http://localhost:3000/api/v1/mock-owner/mock-repo/permission", + ); + jest + .mocked(Octokit) + .mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + // Mock valid token + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, // 1 hour from now + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with permission when authenticated", async () => { + mockOctokitInstance.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue( + { + data: mockPermissionWithoutExtraField, + }, + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect( + mockOctokitInstance.rest.repos.getCollaboratorPermissionLevel, + ).toHaveBeenCalledWith({ + owner: mockContext.owner, + repo: mockContext.repo, + username: "testuser", + }); + + const data: unknown = await response.json(); + expect(data).not.toBeNull(); + expect(typeof data).toBe("object"); + }); + + it("should filter permission using CollaboratorPermsSchema", async () => { + mockOctokitInstance.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue( + { + data: mockPermission, + }, + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + const data = (await response.json()) as Record; + expect(data).not.toHaveProperty("extraField"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + const mockInvalidPermission = { + // Invalid data that will fail CollaboratorPermsSchema.parse + permission: 123, // Should be string + role_name: "admin", + user: getDefaultUser(), + }; + + mockOctokitInstance.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue( + { + data: mockInvalidPermission, + }, + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.repos.getCollaboratorPermissionLevel.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout(() => resolve(mockContext), 0); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.test.ts b/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.test.ts new file mode 100644 index 000000000..db9bae701 --- /dev/null +++ b/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.test.ts @@ -0,0 +1,330 @@ +/* +UNIT TESTS +/api/v1/{owner}/{repo}/pulls +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultUser } from "@/mocks/tests/users"; +import { getDefaultPull } from "@/mocks/tests/pulls"; +import { getDefaultBranch } from "@/mocks/tests/branches"; + +// Mock next-auth/jwt +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +// Mock octokit +jest.mock("octokit", () => ({ + RequestError: jest.fn(), // Added this to avoid undefined error + Octokit: jest.fn(), // Mocked in the beforeEach() +})); + +// Define types for our mocks +interface MockOctokitInstance { + rest: { + pulls: { + list: jest.Mock; + }; + }; +} + +describe("GET /api/v1/{owner}/{repo}/pulls", () => { + const mockPullRequests = [getDefaultPull()]; + const mockOctokitInstance: MockOctokitInstance = { + rest: { + pulls: { + list: jest.fn(), + }, + }, + }; + let mockRequest: Request; + + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Create a mock request + mockRequest = new Request( + "http://localhost:3000/api/v1/mock-owner/mock-repo/pulls", + ); + jest + .mocked(Octokit) + .mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + // Mock valid token + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, // 1 hour from now + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with pull requests when authenticated", async () => { + mockOctokitInstance.rest.pulls.list.mockResolvedValue({ + data: mockPullRequests, + }); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect(mockOctokitInstance.rest.pulls.list).toHaveBeenCalledWith({ + owner: "mock-owner", + repo: "mock-repo", + }); + + const data: unknown = await response.json(); + expect(Array.isArray(data)).toBe(true); + }); + + it("should filter pull requests using PullRequestSchema", async () => { + mockOctokitInstance.rest.pulls.list.mockResolvedValue({ + data: [ + { + url: "", + id: 0, + html_url: "", + number: 0, + state: "open", + locked: false, + title: "", + user: getDefaultUser(), + body: "", + created_at: "", + updated_at: "", + closed_at: null, + merged_at: null, + assignees: [], + requested_reviewers: [], + head: getDefaultBranch(), + base: getDefaultBranch(), + author_association: "CONTRIBUTOR", + draft: false, + assignee: null, + extraField: "blah", + }, + ], + }); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + const data = (await response.json()) as Record[]; + + expect(data[0]).not.toHaveProperty("extraField"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + const mockRepos = [ + { + // Invalid data that will fail PullRequestSchema.parse + id: "invalid-id", // Should be number + }, + ]; + + mockOctokitInstance.rest.pulls.list.mockResolvedValue( + { + data: mockRepos, + }, + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.pulls.list.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest, { + params: new Promise<{ + owner: string; + repo: string; + }>((resolve) => { + setTimeout( + () => resolve({ owner: "mock-owner", repo: "mock-repo" }), + 0, + ); + }), + }); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.ts b/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.ts index 6308901f9..c5d7cc481 100644 --- a/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.ts +++ b/code-review-management/app/api/v1/[owner]/[repo]/pulls/route.ts @@ -32,7 +32,7 @@ export async function GET(req: Request, context: RouteContext) { // Validate token if (token == null || token.accessToken == null || token.githubId == null) { - console.log("Unauthorized request at ${new Date()}"); + console.log(`Unauthorized request at ${new Date()}`); return new Response(null, { status: 401 }); } diff --git a/code-review-management/app/api/v1/repos/route.test.ts b/code-review-management/app/api/v1/repos/route.test.ts new file mode 100644 index 000000000..fabd0855a --- /dev/null +++ b/code-review-management/app/api/v1/repos/route.test.ts @@ -0,0 +1,233 @@ +/* +UNIT TESTS +/api/v1/repos +*/ + +import { GET } from "./route"; +import { Octokit } from "octokit"; +import { getToken, JWT } from "next-auth/jwt"; +import { getDefaultRepo } from "@/mocks/tests/repos"; +import { getDefaultUser } from "@/mocks/tests/users"; + +// Mock next-auth/jwt +jest.mock("next-auth/jwt", () => ({ + getToken: jest.fn(), +})); + +// Mock octokit +jest.mock("octokit", () => ({ + RequestError: jest.fn(), // Added this to avoid undefined error + Octokit: jest.fn(), // Mocked in the beforeEach() +})); + +// Define types for our mocks +interface MockOctokitInstance { + rest: { + repos: { + listForAuthenticatedUser: jest.Mock; + }; + }; +} + +describe("GET /api/v1/repos", () => { + const mockRepos = [getDefaultRepo()]; + const mockOctokitInstance: MockOctokitInstance = { + rest: { + repos: { + listForAuthenticatedUser: jest.fn(), + }, + }, + }; + let mockRequest: Request; + + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Create a mock request + mockRequest = new Request("http://localhost:3000/api/v1/repos"); + jest + .mocked(Octokit) + .mockImplementation(() => mockOctokitInstance as unknown as Octokit); + }); + + describe("Authentication", () => { + it("should return 401 when token is null", async () => { + jest.mocked(getToken).mockResolvedValue(null); + + const response = await GET(mockRequest); + + expect(response.status).toBe(401); + expect(getToken).toHaveBeenCalledWith({ + req: mockRequest, + secret: undefined, + cookieName: "authjs.session-token", + }); + }); + + it("should return 401 when accessToken is undefined", async () => { + const mockToken: JWT = { + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest); + + expect(response.status).toBe(401); + }); + + it("should return 401 when accessToken is null", async () => { + const mockToken: JWT = { + accessToken: undefined, + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is null", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: null, + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest); + + expect(response.status).toBe(401); + }); + + it("should return 401 when githubId is undefined", async () => { + const mockToken: JWT = { + accessToken: "valid-token", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + + const response = await GET(mockRequest); + + expect(response.status).toBe(401); + }); + }); + + describe("Successful requests", () => { + beforeEach(() => { + // Mock valid token + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + expiresAt: Date.now() + 3600000, // 1 hour from now + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 200 with repos when authenticated", async () => { + mockOctokitInstance.rest.repos.listForAuthenticatedUser.mockResolvedValue( + { + data: mockRepos, + }, + ); + + const response = await GET(mockRequest); + + expect(response.status).toBe(200); + expect(jest.mocked(Octokit)).toHaveBeenCalledWith({ + auth: "valid-token", + }); + expect( + mockOctokitInstance.rest.repos.listForAuthenticatedUser, + ).toHaveBeenCalled(); + + const data: unknown = await response.json(); + expect(Array.isArray(data)).toBe(true); + }); + + it("should filter repos using RepoSchema", async () => { + mockOctokitInstance.rest.repos.listForAuthenticatedUser.mockResolvedValue( + { + data: [ + { + id: 0, + name: "", + full_name: "", + owner: getDefaultUser(), + html_url: "", + description: "", + created_at: "", + updated_at: "", + pushed_at: "", + stargazers_count: 0, + watchers_count: 0, + open_issues_count: 0, + has_pull_requests: true, + visibility: "public", + extraField: "blah", + }, + ], + }, + ); + + const response = await GET(mockRequest); + const data = (await response.json()) as Record[]; + + expect(data[0]).not.toHaveProperty("extraField"); + }); + }); + + describe("Error handling", () => { + beforeEach(() => { + const mockToken: JWT = { + accessToken: "valid-token", + githubId: "12345", + githubLogin: "testuser", + }; + + jest.mocked(getToken).mockResolvedValue(mockToken); + }); + + it("should return 500 for parsing errors", async () => { + const mockRepos = [ + { + // Invalid data that will fail RepoSchema.parse + id: "invalid-id", // Should be number + }, + ]; + + mockOctokitInstance.rest.repos.listForAuthenticatedUser.mockResolvedValue( + { + data: mockRepos, + }, + ); + + const response = await GET(mockRequest); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + + it("should return 500 for unknown errors", async () => { + mockOctokitInstance.rest.repos.listForAuthenticatedUser.mockRejectedValue( + new Error("Unknown error"), + ); + + const response = await GET(mockRequest); + + expect(response.status).toBe(500); + const text = await response.text(); + expect(text).toBe("Server error"); + }); + }); +}); diff --git a/code-review-management/app/api/v1/repos/route.ts b/code-review-management/app/api/v1/repos/route.ts index 4851a27c7..173d33098 100644 --- a/code-review-management/app/api/v1/repos/route.ts +++ b/code-review-management/app/api/v1/repos/route.ts @@ -21,7 +21,7 @@ export async function GET(req: Request) { // Validate token if (token == null || token.accessToken == null || token.githubId == null) { - console.log("Unauthorized request at ${new Date()}"); + console.log(`Unauthorized request at ${new Date()}`); return new Response(null, { status: 401 }); } @@ -48,6 +48,7 @@ export async function GET(req: Request) { return new Response(error.message, { status: error.status }); } else { // Parsing/other error + console.log(error); return new Response("Server error", { status: 500 }); } } diff --git a/code-review-management/jest.config.ts b/code-review-management/jest.config.ts index 5590500a3..8ae7745f7 100644 --- a/code-review-management/jest.config.ts +++ b/code-review-management/jest.config.ts @@ -13,7 +13,7 @@ const config: Config = { coveragePathIgnorePatterns: ["mocks", "node_modules"], coverageDirectory: "coverage", coverageProvider: "v8", - testEnvironment: "jest-environment-jsdom", + testEnvironment: "./lib/jsdom-environment.ts", moduleNameMapper: { "^@/(.*)$": "/$1", "^@components/(.*)$": "/app/(pages)/_components/$1", diff --git a/code-review-management/lib/jsdom-environment.ts b/code-review-management/lib/jsdom-environment.ts new file mode 100644 index 000000000..d66a3d138 --- /dev/null +++ b/code-review-management/lib/jsdom-environment.ts @@ -0,0 +1,16 @@ +import JSDOMEnvironment from "jest-environment-jsdom"; + +/** + * Docs: https://stackoverflow.com/a/77258896 + */ + +export default class FixJSDOMEnvironment extends JSDOMEnvironment { + constructor(...args: ConstructorParameters) { + super(...args); + + this.global.fetch = fetch; + this.global.Headers = Headers; + this.global.Request = Request; + this.global.Response = Response; + } +} diff --git a/code-review-management/package.json b/code-review-management/package.json index a7b0af301..cd69fd7ed 100644 --- a/code-review-management/package.json +++ b/code-review-management/package.json @@ -7,7 +7,8 @@ "build": "next build", "start": "next start", "lint": "eslint", - "test": "jest" + "test": "jest", + "test-silent": "jest --silent" }, "dependencies": { "@bprogress/next": "^3.2.12",