diff --git a/src/components/__tests__/ProfileCard.test.tsx b/src/components/__tests__/ProfileCard.test.tsx
new file mode 100644
index 0000000..9a049ae
--- /dev/null
+++ b/src/components/__tests__/ProfileCard.test.tsx
@@ -0,0 +1,123 @@
+// @vitest-environment jsdom
+import { describe, it, expect } from "vitest";
+import { render, screen } from "@testing-library/react";
+import "@testing-library/jest-dom";
+import ProfileCard from "../ProfileCard";
+import type { UserProfile } from "@/lib/types";
+
+const mockProfile: UserProfile = {
+ login: "octocat",
+ avatar_url: "https://github.com/images/error/octocat_happy.gif",
+ name: "The Octocat",
+ bio: "I'm a GitHub mascot.",
+ company: "@github",
+ location: "San Francisco",
+ blog: "https://github.blog",
+ twitter_username: "github",
+ created_at: "2011-01-25T18:44:36Z",
+ followers: 1000,
+ following: 50,
+ public_repos: 100,
+ orgs: [
+ { login: "github", avatar_url: "https://avatars.githubusercontent.com/u/9919?v=4" }
+ ],
+ pinnedRepos: [
+ {
+ name: "Spoon-Knife",
+ description: "This repo is for demonstration purposes only.",
+ url: "https://github.com/octocat/Spoon-Knife",
+ stargazerCount: 15000,
+ primaryLanguage: { name: "HTML", color: "#e34c26" }
+ }
+ ]
+};
+
+describe("ProfileCard", () => {
+ it("renders basic profile information correctly", () => {
+ render();
+
+ // Name and login
+ expect(screen.getByText("The Octocat")).toBeInTheDocument();
+ expect(screen.getByText("@octocat")).toBeInTheDocument();
+
+ // Stats
+ expect(screen.getByText(mockProfile.followers.toLocaleString())).toBeInTheDocument();
+ expect(screen.getByText(mockProfile.following.toLocaleString())).toBeInTheDocument();
+ expect(screen.getByText(mockProfile.public_repos.toLocaleString())).toBeInTheDocument();
+
+ // Join date - "Joined January 2011"
+ expect(screen.getByText(/Joined January 2011/)).toBeInTheDocument();
+ });
+
+ it("falls back to login when name is null", () => {
+ const profileWithoutName = { ...mockProfile, name: null };
+ render();
+
+ expect(screen.getByRole("heading", { level: 2, name: "octocat" })).toBeInTheDocument();
+ expect(screen.getByText("@octocat")).toBeInTheDocument();
+ });
+
+ it("renders optional fields when provided", () => {
+ render();
+
+ expect(screen.getByText("I'm a GitHub mascot.")).toBeInTheDocument();
+ expect(screen.getByText("@github")).toBeInTheDocument();
+ expect(screen.getByText("San Francisco")).toBeInTheDocument();
+ expect(screen.getByText("github.blog")).toBeInTheDocument();
+ });
+
+ it("does not render optional fields when null", () => {
+ const minimalProfile = {
+ ...mockProfile,
+ bio: null,
+ company: null,
+ location: null,
+ blog: null,
+ orgs: [],
+ pinnedRepos: []
+ };
+ render();
+
+ expect(screen.queryByText("I'm a GitHub mascot.")).not.toBeInTheDocument();
+ expect(screen.queryByText("@github")).not.toBeInTheDocument();
+ expect(screen.queryByText("San Francisco")).not.toBeInTheDocument();
+ expect(screen.queryByText("github.blog")).not.toBeInTheDocument();
+ expect(screen.queryByText("Organizations")).not.toBeInTheDocument();
+ expect(screen.queryByText("Pinned Repositories")).not.toBeInTheDocument();
+ });
+
+ it("renders organizations correctly", () => {
+ render();
+ screen.getByText("Organizations");
+ screen.getByText("github");
+ });
+
+ it("renders pinned repositories correctly", () => {
+ render();
+ expect(screen.getByText("Pinned Repositories")).toBeInTheDocument();
+ expect(screen.getByText("Spoon-Knife")).toBeInTheDocument();
+ expect(screen.getByText("This repo is for demonstration purposes only.")).toBeInTheDocument();
+ expect(screen.getByText("HTML")).toBeInTheDocument();
+ expect(screen.getByText(mockProfile.pinnedRepos[0].stargazerCount.toLocaleString())).toBeInTheDocument();
+ });
+
+ it("handles pinned repos without description or language", () => {
+ const profileWithMinimalRepo = {
+ ...mockProfile,
+ pinnedRepos: [
+ {
+ name: "Empty-Repo",
+ description: null,
+ url: "https://github.com/octocat/Empty-Repo",
+ stargazerCount: 0,
+ primaryLanguage: null
+ }
+ ]
+ };
+ render();
+
+ expect(screen.getByText("Pinned Repositories")).toBeInTheDocument();
+ expect(screen.getByText("Empty-Repo")).toBeInTheDocument();
+ expect(screen.getByText("0")).toBeInTheDocument();
+ });
+});