{"id":31158098,"url":"https://github.com/arnobt78/explore-github-users-react-vitest-tutorial","last_synced_at":"2025-09-18T23:31:18.119Z","repository":{"id":304265270,"uuid":"1018285063","full_name":"arnobt78/Explore-Github-Users-React-Vitest-Tutorial","owner":"arnobt78","description":"A modern web application to search and visualize GitHub user profiles, repositories, and statistics. Built with React, Apollo Client, GraphQL, Vite, Tailwind CSS, and a comprehensive suite of automated tests.","archived":false,"fork":false,"pushed_at":"2025-07-12T00:33:58.000Z","size":122,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-12T03:01:27.224Z","etag":null,"topics":["api-testing","apollo-client","github-graphql-api","github-user-explorer","github-user-search","graphql","mock-service-worker","mock-service-worker-testing","msw","react-testing","react-testing-github","react-testing-library","react-vite-tailwindcss-typescript","rtl","search-form","stats-charts","test-automation","user-profile","vitest","vitest-ts"],"latest_commit_sha":null,"homepage":"https://search-github-users-explorer.netlify.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arnobt78.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-12T00:24:20.000Z","updated_at":"2025-07-12T00:46:18.000Z","dependencies_parsed_at":"2025-07-13T03:18:01.274Z","dependency_job_id":null,"html_url":"https://github.com/arnobt78/Explore-Github-Users-React-Vitest-Tutorial","commit_stats":null,"previous_names":["arnobt78/explore-github-users-react-vitest-tutorial"],"tags_count":null,"template":true,"template_full_name":null,"purl":"pkg:github/arnobt78/Explore-Github-Users-React-Vitest-Tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FExplore-Github-Users-React-Vitest-Tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FExplore-Github-Users-React-Vitest-Tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FExplore-Github-Users-React-Vitest-Tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FExplore-Github-Users-React-Vitest-Tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arnobt78","download_url":"https://codeload.github.com/arnobt78/Explore-Github-Users-React-Vitest-Tutorial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arnobt78%2FExplore-Github-Users-React-Vitest-Tutorial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275852107,"owners_count":25540136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-18T02:00:09.552Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-testing","apollo-client","github-graphql-api","github-user-explorer","github-user-search","graphql","mock-service-worker","mock-service-worker-testing","msw","react-testing","react-testing-github","react-testing-library","react-vite-tailwindcss-typescript","rtl","search-form","stats-charts","test-automation","user-profile","vitest","vitest-ts"],"created_at":"2025-09-18T23:31:14.717Z","updated_at":"2025-09-18T23:31:18.080Z","avatar_url":"https://github.com/arnobt78.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Search/Explore Github Users - React Vitest Tutorial\n\n![Screenshot 2025-06-18 at 14 39 05](https://github.com/user-attachments/assets/1f0df645-e214-4b44-b2c6-1e3ddb5647c3)\n![Screenshot 2025-06-18 at 14 38 30](https://github.com/user-attachments/assets/d0784a42-6de3-4030-a852-3f612cf8234c)\n\n---\n\nA modern web application to search and visualize GitHub user profiles, repositories, and statistics. Built with React, Apollo Client, GraphQL, Vite, Tailwind CSS, and a comprehensive suite of automated tests.\n\n- **Live-Demo:** [https://search-github-users-explorer.netlify.app/](https://search-github-users-explorer.netlify.app/)\n\n---\n\n## Table of Contents\n\n- [Project Summary](#project-summary)\n- [Features](#features)\n- [Technology Stack](#technology-stack)\n- [Project Structure](#project-structure)\n- [Setup \u0026 Installation](#setup--installation)\n- [Environment Variables](#environment-variables)\n- [Usage](#usage)\n- [Functionality Walkthrough](#functionality-walkthrough)\n- [Testing](#testing)\n- [Keywords](#keywords)\n- [Conclusion](#conclusion)\n\n---\n\n## Project Summary\n\n**Explore Github Users** is a React-based web app that allows users to search for any GitHub username and view detailed profile information, repository statistics, and visualizations. The app leverages the GitHub GraphQL API for efficient data fetching and provides a modern UI with Tailwind CSS and shadcn/ui components. Comprehensive automated tests ensure reliability and serve as a learning resource for React Testing Library, Vitest, and MSW.\n\n---\n\n## Features\n\n- Search for any GitHub user by username\n- View user profile details (avatar, name, bio, profile link)\n- See repository statistics: total repos, followers, following, gists\n- Visualize most used languages, most starred, and most forked repositories with charts\n- Responsive, accessible, and modern UI\n- Toast notifications for user feedback\n- Robust error handling for invalid users and network issues\n- Comprehensive automated tests for all major components and utilities\n\n---\n\n## Technology Stack\n\n- **React** (with Vite)\n- **TypeScript**\n- **Tailwind CSS**\n- **shadcn/ui** (UI components)\n- **Apollo Client** (GraphQL client)\n- **GitHub GraphQL API**\n- **Vitest** (test runner)\n- **React Testing Library**\n- **MSW** (Mock Service Worker for API mocking)\n\n---\n\n## Project Structure\n\n```bash\n├── public/\n├── src/\n│   ├── apolloClient.ts\n│   ├── App.tsx\n│   ├── index.css\n│   ├── main.tsx\n│   ├── queries.ts\n│   ├── types.ts\n│   ├── utils.ts\n│   ├── __tests__/\n│   │   ├── App.test.tsx\n│   │   ├── ForkedRepos.test.tsx\n│   │   ├── SearchForm.test.tsx\n│   │   ├── StatsCard.test.tsx\n│   │   ├── StatsContainer.test.tsx\n│   │   ├── UserCard.test.tsx\n│   │   ├── UserProfile.test.tsx\n│   │   └── utils.ts\n│   ├── assets/\n│   ├── components/\n│   │   ├── charts/\n│   │   │   ├── ForkedRepos.tsx\n│   │   │   ├── PopularRepos.tsx\n│   │   │   └── UsedLanguages.tsx\n│   │   ├── form/\n│   │   │   └── SearchForm.tsx\n│   │   ├── ui/\n│   │   ├── user/\n│   │   │   ├── Loading.tsx\n│   │   │   ├── StatsCard.tsx\n│   │   │   ├── StatsContainer.tsx\n│   │   │   ├── UserCard.tsx\n│   │   │   └── UserProfile.tsx\n│   ├── hooks/\n│   ├── lib/\n│   ├── mocks/\n│   │   ├── handlers.ts\n│   │   └── server.ts\n├── package.json\n├── tailwind.config.js\n├── vite.config.ts\n├── tsconfig.json\n└── README.md\n```\n\n---\n\n## Setup \u0026 Installation\n\n1. **Clone the repository**\n2. **Install dependencies**\n\n   ```bash\n   npm install\n   ```\n\n3. **Run the development server**\n\n   ```bash\n   npm run dev\n   ```\n\n### Tailwind CSS Setup\n\n- Already configured in this project. For reference:\n\n  ```bash\n  npm install -D tailwindcss postcss autoprefixer\n  npx tailwindcss init -p\n  ```\n\n  - Add Tailwind directives to `index.css`:\n\n    ```css\n    @tailwind base;\n    @tailwind components;\n    @tailwind utilities;\n    ```\n\n### shadcn/ui Setup\n\n- Already configured. To add more components:\n\n  ```bash\n  npx shadcn@latest add button card chart input label skeleton toast\n  ```\n\n---\n\n## Environment Variables\n\nYou need a GitHub Personal Access Token to use the GitHub GraphQL API. Create a `.env.local` file in the project root:\n\n```bash\nVITE_GITHUB_TOKEN=YOUR_TOKEN_HERE\n```\n\n- [How to create a GitHub token](https://github.com/settings/tokens)\n\n---\n\n## Usage\n\n- Start the app: `npm run dev`\n- Enter a GitHub username in the search bar and submit\n- View user profile, stats, and charts\n- Error messages and toasts will appear for invalid usernames or network issues\n\n---\n\n## Functionality Walkthrough\n\n### 1. **Search Form**\n\n- Located at `src/components/form/SearchForm.tsx`\n- Controlled input for username, with validation and toast feedback\n- See the full code example in the [SearchForm component section below](#search-form)\n\n### 2. **User Profile**\n\n- Located at `src/components/user/UserProfile.tsx`\n- Fetches user data via Apollo Client and displays profile, stats, and charts\n- Handles loading, error, and not-found states\n\n### 3. **Stats \u0026 Charts**\n\n- Stats: `StatsCard` and `StatsContainer` components\n- Charts: `UsedLanguages`, `PopularRepos`, `ForkedRepos` (in `components/charts/`)\n- Utility functions in `src/utils.ts` process repository data for charting\n\n### 4. **Apollo Client \u0026 GraphQL**\n\n- Apollo Client setup in `src/apolloClient.ts`\n- GraphQL query in `src/queries.ts`\n- Types in `src/types.ts`\n\n### 5. **UI \u0026 Feedback**\n\n- Modern UI with Tailwind and shadcn/ui\n- Toast notifications for user feedback\n- Skeleton loaders for async states\n\n---\n\n## Testing\n\nThis project includes a full suite of automated tests using **Vitest**, **React Testing Library**, and **MSW**. All major components, utilities, and integration flows are covered.\n\n### Test Structure\n\n- All tests are in `src/__tests__/`\n- Includes unit tests for utilities and components, and integration tests for user flows\n- MSW is used to mock GraphQL API responses\n\n### Running Tests\n\n```bash\nnpx vitest\n```\n\n### Example Test Files\n\n- `App.test.tsx`: Integration tests for the main app flow\n- `UserProfile.test.tsx`: Tests for user profile fetching and error handling\n- `SearchForm.test.tsx`: Tests for form validation and user interaction\n- `StatsCard.test.tsx`, `StatsContainer.test.tsx`, `UserCard.test.tsx`: Unit tests for UI components\n- `ForkedRepos.test.tsx`: Tests for chart rendering\n- `utils.ts`: Unit tests for repository statistics utilities\n\n### Testing Highlights\n\n- All error scenarios (invalid user, network error) are tested\n- Mock Service Worker (MSW) intercepts GraphQL queries for reliable, isolated tests\n- All tests are passing (see test output in project logs)\n\n---\n\n## Code Changes \u0026 Additions\n\n### App.tsx\n\n```tsx\nconst App = () =\u003e {\n  return \u003ch1 className=\"text-2xl font-bold\"\u003eSearch Github Users\u003c/h1\u003e;\n};\nexport default App;\n```\n\n- remove App.css\n- change title in index.html\n\n```html\n\u003ctitle\u003eSearch Github Users\u003c/title\u003e\n```\n\n### Shadcn UI\n\ntsconfig.json\n\n```json\n{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ],\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\ntsconfig.app.json\n\n```json\n{\n  \"compilerOptions\": {\n    // rest of the options\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n```bash\nnpm i -D @types/node\n\n```\n\nvite.config.ts\n\n```ts\nimport path from \"path\";\nimport react from \"@vitejs/plugin-react\";\nimport { defineConfig } from \"vite\";\n\nexport default defineConfig({\n  plugins: [react()],\n  resolve: {\n    alias: {\n      \"@\": path.resolve(__dirname, \"./src\"),\n    },\n  },\n});\n```\n\n- initialize shadcn\n\n```bash\nnpx shadcn@latest init\n```\n\n- add components\n\n```bash\nnpx shadcn@latest add button card chart input label skeleton toast\n```\n\nApp.tsx\n\n```tsx\nimport { Button } from \"@/components/ui/button\";\nconst App = () =\u003e {\n  return (\n    \u003cdiv className=\"flex  items-center justify-center h-screen\"\u003e\n      \u003cdiv className=\"flex gap-4\"\u003e\n        \u003cButton\u003eClick me\u003c/Button\u003e\n        \u003cButton variant=\"outline\" size=\"lg\"\u003e\n          Click me\n        \u003c/Button\u003e\n        \u003cButton variant=\"destructive\" size=\"sm\"\u003e\n          Click me\n        \u003c/Button\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\nexport default App;\n```\n\n### Structure\n\n- setup local state in App.tsx\n- create src/components/form/SearchForm.tsx\n- create src/components/user/UserProfile.tsx\n- render both components in App.tsx\n- pass userName and setUserName to SearchForm\n- pass userName to UserProfile\n\nApp.tsx\n\n```tsx\nconst [userName, setUserName] = useState(\"quincylarson\");\n```\n\nsrc/components/form/SearchForm.tsx\n\n```tsx\ntype SearchFormProps = {\n  userName: string;\n  setUserName: React.Dispatch\u003cReact.SetStateAction\u003cstring\u003e\u003e;\n};\n\nconst SearchForm = ({ userName, setUserName }: SearchFormProps) =\u003e {\n  return \u003cdiv\u003eSearchForm\u003c/div\u003e;\n};\nexport default SearchForm;\n```\n\nsrc/components/user/UserProfile.tsx\n\n```tsx\ntype UserProfileProps = {\n  userName: string;\n};\n\nconst UserProfile = ({ userName }: UserProfileProps) =\u003e {\n  return \u003ch1 className=\"text-2xl font-bold\"\u003e{userName}\u003c/h1\u003e;\n};\nexport default UserProfile;\n```\n\nsrc/App.tsx\n\n```tsx\nmport { useState } from 'react';\nimport SearchForm from './components/form/SearchForm';\nimport UserProfile from './components/user/UserProfile';\n\nconst App = () =\u003e {\n  const [userName, setUserName] = useState('quincylarson');\n\n  return (\n    \u003cmain className='mx-auto max-w-6xl px-8 py-20'\u003e\n      \u003cSearchForm userName={userName} setUserName={setUserName} /\u003e\n      \u003cUserProfile userName={userName} /\u003e\n    \u003c/main\u003e\n  );\n};\nexport default App;\n```\n\n### Search Form\n\n```tsx\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { type FormEvent } from \"react\";\nimport { useState } from \"react\";\n\ntype SearchFormProps = {\n  userName: string;\n  setUserName: React.Dispatch\u003cReact.SetStateAction\u003cstring\u003e\u003e;\n};\n\nconst SearchForm = ({ userName, setUserName }: SearchFormProps) =\u003e {\n  const [text, setText] = useState(userName);\n\n  const handleSearch = (e: FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n    e.preventDefault();\n    if (text === \"\") {\n      console.log(\"Please enter a username\");\n      return;\n    }\n    setUserName(text);\n  };\n\n  return (\n    \u003cform\n      onSubmit={handleSearch}\n      className=\"flex items-center gap-x-2 w-full lg:w-1/3 mb-8\"\n    \u003e\n      \u003cLabel htmlFor=\"search\" className=\"sr-only\"\u003e\n        Search\n      \u003c/Label\u003e\n      \u003cInput\n        type=\"text\"\n        id=\"search\"\n        value={text}\n        onChange={(e) =\u003e setText(e.target.value)}\n        placeholder=\"Search Github User...\"\n        className=\"flex-grow bg-background\"\n      /\u003e\n      \u003cButton type=\"submit\"\u003eSearch\u003c/Button\u003e\n    \u003c/form\u003e\n  );\n};\nexport default SearchForm;\n```\n\n### Shadcn Toast\n\nmain.tsx\n\n```tsx\nimport { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./index.css\";\nimport App from \"./App.tsx\";\n// import Toaster component\nimport { Toaster } from \"@/components/ui/toaster\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n  \u003cStrictMode\u003e\n    \u003cApp /\u003e\n    \u003cToaster /\u003e\n  \u003c/StrictMode\u003e\n);\n```\n\nsrc/components/form/SearchForm.tsx\n\n```tsx\nimport { useToast } from \"@/hooks/use-toast\";\n\nconst SearchForm = ({ userName, setUserName }: SearchFormProps) =\u003e {\n  const { toast } = useToast();\n\n  const handleSearch = (e: FormEvent\u003cHTMLFormElement\u003e) =\u003e {\n    e.preventDefault();\n    if (text === \"\") {\n      toast({\n        description: \"Please enter a valid username\",\n      });\n      return;\n    }\n    setUserName(text);\n  };\n\n  return \u003cform\u003e...\u003c/form\u003e;\n};\nexport default SearchForm;\n```\n\n### Graphql\n\nGraphQL is a modern query language and runtime for APIs that allows clients to request specific data they need and nothing more. Unlike traditional REST APIs where you get fixed data from multiple endpoints, GraphQL provides a single endpoint where you can specify exactly what data you want to receive.\n\n- **Schema**: The blueprint that defines all available data types and operations in your API\n- **Query**: A request to read or fetch data (similar to GET in REST)\n- **Mutation**: A request to create, update, or delete data (similar to POST/PUT/DELETE in REST)\n- **Fields**: The individual pieces of data you can request (like user.name or post.title)\n- **Arguments**: Parameters you can pass to fields to filter or modify the results (like limit: 10)\n- **Types**: The different kinds of data objects available (like User, Post, Comment)\n- **Nodes**: Objects in a GraphQL schema that have a unique identifier, typically representing entities in your data model (like a specific user or post)\n\n[Practice API's](https://www.apollographql.com/blog/8-free-to-use-graphql-apis-for-your-projects-and-demos)\n\n### Github GraphQL Explorer\n\n[Github GraphQL Explorer](https://docs.github.com/en/graphql/overview/explorer)\n\n### Github Personal Access Token\n\n[Github](https://github.com/)\n\n- profile\n- settings\n- developer settings\n- personal access token\n- generate new token\n- create .env.local file\n- add token to .env.local file\n\n.env.local\n\n```bash\nVITE_GITHUB_TOKEN=YOUR_TOKEN_HERE\n```\n\n### Apollo Client\n\nApollo Client is a comprehensive state management library for JavaScript applications that helps you manage both local and remote data with GraphQL. It makes it easy to fetch, cache, and modify application data while automatically handling important concerns like tracking loading and error states. The library integrates especially well with React applications and provides features like automatic caching, optimistic UI updates, and error handling out of the box.\n\n[Apollo Client](https://www.apollographql.com/docs/react/get-started/)\n\n```bash\nnpm install @apollo/client graphql\n```\n\n- src/apolloClient.ts\n\n```ts\n// Core Apollo Client imports for GraphQL functionality\n// ApolloClient: Main client class for making GraphQL requests\n// InMemoryCache: Caching solution for storing query results\n// HttpLink: Configures HTTP connection to GraphQL endpoint\n// ApolloLink: Enables creation of middleware chain for request/response handling\nimport {\n  ApolloClient,\n  InMemoryCache,\n  HttpLink,\n  ApolloLink,\n} from \"@apollo/client\";\n\n// Error handling middleware for Apollo Client\n// Provides detailed error information for both GraphQL and network errors\nimport { onError } from \"@apollo/client/link/error\";\n\n// GitHub GraphQL API endpoint\nconst GITHUB_GRAPHQL_API = \"https://api.github.com/graphql\";\n\n// Configure error handling middleware\n// This will intercept and log any GraphQL or network errors\nconst errorLink = onError(({ graphQLErrors, networkError }) =\u003e {\n  // Handle GraphQL-specific errors (e.g., validation, resolver errors)\n  if (graphQLErrors) {\n    graphQLErrors.forEach(({ message, locations, path }) =\u003e {\n      console.error(\n        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`\n      );\n    });\n  }\n\n  // Handle network-level errors (e.g., connection issues)\n  if (networkError) {\n    console.error(`[Network error]: ${networkError}`);\n  }\n});\n\n// Configure HTTP connection to GitHub's GraphQL API\n// Including authentication token from environment variables\nconst httpLink = new HttpLink({\n  uri: GITHUB_GRAPHQL_API,\n  headers: {\n    Authorization: `Bearer ${import.meta.env.VITE_GITHUB_TOKEN}`, // GitHub Personal Access Token\n  },\n});\n\n// Create the Apollo Link chain\n// Order matters: errorLink will run before httpLink\nconst link = ApolloLink.from([errorLink, httpLink]);\n\n// Initialize Apollo Client with:\n// - Configured link chain for network requests\n// - In-memory cache for storing query results\nconst client = new ApolloClient({\n  link,\n  cache: new InMemoryCache(),\n});\n\nexport default client;\n```\n\nsrc/main.tsx\n\n```tsx\nimport { createRoot } from \"react-dom/client\";\nimport App from \"./App.tsx\";\nimport \"./index.css\";\nimport { Toaster } from \"@/components/ui/toaster\";\n// Apollo Provider\nimport { ApolloProvider } from \"@apollo/client\";\nimport client from \"./apolloClient\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n  \u003cApolloProvider client={client}\u003e\n    \u003cApp /\u003e\n    \u003cToaster /\u003e\n  \u003c/ApolloProvider\u003e\n);\n```\n\n### Query and Type\n\nsrc/queries.ts\n\n```ts\nimport { gql } from \"@apollo/client\";\n\nexport const GET_USER = gql`\n  query ($login: String!) {\n    user(login: $login) {\n      name\n      avatarUrl\n      bio\n      url\n      repositories(first: 100) {\n        totalCount\n        nodes {\n          name\n          description\n          stargazerCount\n          forkCount\n          url\n          languages(first: 5) {\n            edges {\n              node {\n                name\n              }\n              size\n            }\n          }\n        }\n      }\n      followers {\n        totalCount\n      }\n      following {\n        totalCount\n      }\n      gists {\n        totalCount\n      }\n    }\n  }\n`;\n```\n\nsrc/types.ts\n\n```ts\nexport type LanguageEdge = {\n  node: {\n    name: string;\n  };\n  size: number;\n};\n\nexport type Repository = {\n  name: string;\n  description: string;\n  stargazerCount: number;\n  forkCount: number;\n  url: string;\n  languages: {\n    edges: LanguageEdge[];\n  };\n};\n\nexport type User = {\n  name: string;\n  avatarUrl: string;\n  bio: string;\n  url: string;\n  repositories: {\n    totalCount: number;\n    nodes: Repository[];\n  };\n  followers: {\n    totalCount: number;\n  };\n  following: {\n    totalCount: number;\n  };\n  gists: {\n    totalCount: number;\n  };\n};\nexport type UserData = {\n  user: User;\n};\n```\n\n### Query Hook\n\nsrc/components/user/UserProfile.tsx\n\n```tsx\nimport { useQuery } from \"@apollo/client\";\nimport { GET_USER } from \"@/queries\";\nimport { UserData } from \"@/types\";\n\ntype UserProfileProps = {\n  userName: string;\n};\n\nconst UserProfile = ({ userName }: UserProfileProps) =\u003e {\n  const { loading, error, data } = useQuery\u003cUserData\u003e(GET_USER, {\n    variables: { login: userName },\n  });\n\n  if (loading) return \u003cdiv\u003eLoading...\u003c/div\u003e;\n  if (error) return \u003ch2 className=\"text-xl\"\u003e{error.message}\u003c/h2\u003e;\n  if (!data) return \u003ch2 className=\"text-xl\"\u003eUser Not Found.\u003c/h2\u003e;\n\n  const {\n    avatarUrl,\n    name,\n    bio,\n    url,\n    repositories,\n    followers,\n    following,\n    gists,\n  } = data.user;\n\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003e{bio}\u003c/h1\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default UserProfile;\n```\n\n### User Card\n\nsrc/components/user/UserCard.tsx\n\n```tsx\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Card,\n  CardTitle,\n  CardDescription,\n  CardHeader,\n} from \"@/components/ui/card\";\n\ntype UserCardProps = {\n  avatarUrl: string;\n  name: string;\n  bio: string;\n  url: string;\n};\nconst UserCard = ({ avatarUrl, name, bio, url }: UserCardProps) =\u003e {\n  return (\n    \u003cCard className=\"w-full lg:w-1/2 mb-8\"\u003e\n      \u003cCardHeader className=\"flex-row gap-x-8 items-center\"\u003e\n        \u003cimg\n          src={avatarUrl}\n          alt={name}\n          className=\"w-36 h-36  rounded object-cover\"\n        /\u003e\n        \u003cdiv className=\"flex flex-col gap-y-2\"\u003e\n          \u003cCardTitle\u003e{name || \"Coding Addict\"}\u003c/CardTitle\u003e\n          \u003cCardDescription\u003e\n            {bio || \"Passionate about coding and technology.\"}\n          \u003c/CardDescription\u003e\n          \u003cButton asChild size=\"sm\" className=\"w-1/2 mt-2\"\u003e\n            \u003ca href={url} target=\"_blank\" rel=\"noreferrer\"\u003e\n              Follow\n            \u003c/a\u003e\n          \u003c/Button\u003e\n        \u003c/div\u003e\n      \u003c/CardHeader\u003e\n    \u003c/Card\u003e\n  );\n};\nexport default UserCard;\n```\n\n- UserProfile.tsx\n\n```tsx\nreturn (\n  \u003cdiv\u003e\n    \u003cUserCard avatarUrl={avatarUrl} name={name} bio={bio} url={url} /\u003e\n  \u003c/div\u003e\n);\n```\n\n### Stats Card\n\n```tsx\nimport { Card, CardTitle, CardDescription } from \"../ui/card\";\n\ntype StatsCardProps = {\n  title: string;\n  count: number;\n};\n\nfunction StatsCard({ title, count }: StatsCardProps) {\n  return (\n    \u003cCard\u003e\n      \u003cdiv className=\"flex flex-row justify-between items-center p-6\"\u003e\n        \u003cCardTitle\u003e{title}\u003c/CardTitle\u003e\n        \u003cCardDescription\u003e{count}\u003c/CardDescription\u003e\n      \u003c/div\u003e\n    \u003c/Card\u003e\n  );\n}\n\nexport default StatsCard;\n```\n\n### Stats Container\n\n```tsx\nimport StatsCard from \"./StatsCard\";\n\ntype StatsContainerProps = {\n  totalRepos: number;\n  followers: number;\n  following: number;\n  gists: number;\n};\n\nconst StatsContainer = (props: StatsContainerProps) =\u003e {\n  const { totalRepos, followers, following, gists } = props;\n\n  return (\n    \u003cdiv className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-2 mb-8 \"\u003e\n      \u003cStatsCard title=\"Total Repositories\" count={totalRepos} /\u003e\n      \u003cStatsCard title=\"Followers\" count={followers} /\u003e\n      \u003cStatsCard title=\"Following\" count={following} /\u003e\n      \u003cStatsCard title=\"Gists\" count={gists} /\u003e\n    \u003c/div\u003e\n  );\n};\nexport default StatsContainer;\n```\n\nUserProfile.tsx\n\n```tsx\nreturn (\n  \u003cdiv\u003e\n    \u003cUserCard avatarUrl={avatarUrl} name={name} bio={bio} url={url} /\u003e\n    \u003cStatsContainer\n      totalRepos={repositories.totalCount}\n      followers={followers.totalCount}\n      following={following.totalCount}\n      gists={gists.totalCount}\n    /\u003e\n  \u003c/div\u003e\n);\n```\n\n### Util Functions\n\nAnd once we are done with the Stats container, we can start working on the charts, but since charts will need very specific data, first we will need to create some util functions to help us generate such data.\n\nsrc/utils.ts\n\n```ts\nimport { Repository } from \"./types\";\n\n/**\n * Calculates the top 5 most forked repositories\n * @param repositories Array of repository data from GitHub API\n * @returns Array of objects containing repository names and their fork counts\n * Example return: [{ repo: \"react\", count: 1000 }, { repo: \"vue\", count: 500 }]\n */\nexport const calculateMostForkedRepos = (\n  repositories: Repository[]\n): { repo: string; count: number }[] =\u003e {\n  if (repositories.length === 0) {\n    return [];\n  }\n\n  // Transform repository data into simplified objects containing only name and fork count\n  const forkedRepos = repositories\n    .map((repo) =\u003e ({\n      repo: repo.name, // Extract repository name\n      count: repo.forkCount, // Extract number of forks\n    }))\n    .sort((a, b) =\u003e b.count - a.count) // Sort by fork count in descending order\n    .slice(0, 5); // Take only the top 5 repositories\n\n  return forkedRepos;\n};\n\n/**\n * Calculates the top 5 most starred repositories\n * @param repositories Array of repository data from GitHub API\n * @returns Array of objects containing repository names and their star counts\n * Example return: [{ repo: \"tensorflow\", stars: 5000 }, { repo: \"linux\", stars: 4000 }]\n */\nexport const calculateMostStarredRepos = (\n  repositories: Repository[]\n): { repo: string; stars: number }[] =\u003e {\n  if (repositories.length === 0) {\n    return [];\n  }\n\n  // Transform repository data into simplified objects containing only name and star count\n  const starredRepos = repositories\n    .map((repo) =\u003e ({\n      repo: repo.name, // Extract repository name\n      stars: repo.stargazerCount, // Extract number of stars (stargazers)\n    }))\n    .sort((a, b) =\u003e b.stars - a.stars) // Sort by star count in descending order\n    .slice(0, 5); // Take only the top 5 repositories\n\n  return starredRepos;\n};\n\n/**\n * Calculates the top 5 most used programming languages across all repositories\n * @param repositories Array of repository data from GitHub API\n * @returns Array of objects containing language names and their occurrence count\n * Example return: [{ language: \"JavaScript\", count: 10 }, { language: \"Python\", count: 7 }]\n */\n\nexport const calculatePopularLanguages = (\n  repositories: Repository[]\n): { language: string; count: number }[] =\u003e {\n  // Return empty array if no repositories are provided\n  if (repositories.length === 0) {\n    return [];\n  }\n\n  // Initialize a map to track how many times each language appears\n  // Example: { \"JavaScript\": 5, \"Python\": 3, \"TypeScript\": 2 }\n  const languageMap: { [key: string]: number } = {};\n\n  repositories.forEach((repo) =\u003e {\n    // Skip repositories with no languages\n    if (repo.languages.edges.length === 0) {\n      return;\n    }\n\n    // Iterate through each language in the repository\n    // languages.edges comes from GitHub's GraphQL API structure\n    repo.languages.edges.forEach((language) =\u003e {\n      const { name } = language.node;\n      // Increment the count for this language, initializing to 1 if it's the first occurrence\n      languageMap[name] = (languageMap[name] || 0) + 1;\n    });\n  });\n\n  // If no languages were found in any repository, return empty array\n  if (Object.keys(languageMap).length === 0) {\n    return [];\n  }\n\n  // Convert the language map into an array of objects and sort them\n  return (\n    Object.entries(languageMap)\n      // Convert entries into array of [language, count] pairs\n      .sort(([, a], [, b]) =\u003e b - a) // Sort by count in descending order\n      .slice(0, 5) // Take only the top 5 languages\n      .map(([language, count]) =\u003e ({ language, count }))\n  ); // Transform into required object format\n};\n```\n\n### Charts\n\n- components/charts/UsedLanguages.tsx\n- components/charts/PopularRepos.tsx\n- components/charts/ForkedRepos.tsx\n\nUserProfile.tsx\n\n```tsx\n{\n  repositories.totalCount \u003e 0 \u0026\u0026 (\n    \u003cdiv className=\"grid md:grid-cols-2 gap-4\"\u003e\n      \u003cUsedLanguages repositories={repositories.nodes} /\u003e\n      \u003cPopularRepos repositories={repositories.nodes} /\u003e\n      \u003cForkedRepos repositories={repositories.nodes} /\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n### Used Languages\n\ncomponents/charts/UsedLanguages.tsx\n\n```tsx\nimport { type Repository } from \"@/types\";\nimport { Bar, BarChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n  ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport { calculatePopularLanguages } from \"@/utils\";\n\nconst UsedLanguages = ({ repositories }: { repositories: Repository[] }) =\u003e {\n  // Calculate popular languages\n  //  [{language: string, count: number}]\n  const popularLanguages = calculatePopularLanguages(repositories);\n\n  // Configuration for the chart's styling and labels\n  // color sets the color of the bars\n\n  const chartConfig = {\n    language: {\n      label: \"Language\",\n      color: \"#2563eb\",\n    },\n  } satisfies ChartConfig;\n  return (\n    \u003cdiv\u003e\n      \u003ch2 className=\"text-2xl font-semibold text-center mb-4\"\u003e\n        Used Languages\n      \u003c/h2\u003e\n      {/* ChartContainer handles responsive sizing and theme variables */}\n      \u003cChartContainer config={chartConfig} className=\"h-100 w-full\"\u003e\n        {/* BarChart is the main container for the bar chart visualization */}\n        {/* accessibilityLayer adds ARIA labels for better screen reader support */}\n        \u003cBarChart accessibilityLayer data={popularLanguages}\u003e\n          {/* CartesianGrid adds horizontal guide lines */}\n          \u003cCartesianGrid vertical={false} /\u003e\n\n          {/* XAxis configures the horizontal axis showing language names */}\n          \u003cXAxis\n            dataKey=\"language\"\n            tickLine={false} // Removes tick marks\n            tickMargin={10} // Adds spacing between labels and axis\n          /\u003e\n\n          {/* YAxis configures the vertical axis showing count values */}\n          \u003cYAxis dataKey=\"count\" /\u003e\n\n          {/* ChartTooltip shows details when hovering over bars */}\n          \u003cChartTooltip content={\u003cChartTooltipContent /\u003e} /\u003e\n\n          {/* Bar component defines how each data point is rendered */}\n          {/* Uses CSS variable for color and adds rounded corners */}\n          \u003cBar dataKey=\"count\" fill=\"var(--color-language)\" radius={4} /\u003e\n        \u003c/BarChart\u003e\n      \u003c/ChartContainer\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default UsedLanguages;\n```\n\n### Popular Repos\n\ncomponents/charts/PopularRepos.tsx\n\n```tsx\nimport { type Repository } from \"@/types\";\nimport { Bar, BarChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n  ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport { calculateMostStarredRepos } from \"@/utils\";\n\nconst PopularRepos = ({ repositories }: { repositories: Repository[] }) =\u003e {\n  // Calculate most starred repositories and return array of {repo: string, stars: number}\n  const popularRepos = calculateMostStarredRepos(repositories);\n\n  // Configuration for the chart's styling and labels\n  const chartConfig = {\n    repo: {\n      label: \"Repository\",\n      color: \"#e11c47\", // Red color for the bars\n    },\n  } satisfies ChartConfig;\n\n  return (\n    \u003cdiv\u003e\n      \u003ch2 className=\"text-2xl font-semibold text-center mb-4\"\u003ePopular Repos\u003c/h2\u003e\n      {/* ChartContainer: Custom wrapper component that handles responsive sizing and theme */}\n      \u003cChartContainer config={chartConfig} className=\"h-100 w-full\"\u003e\n        {/* BarChart: Main chart component from recharts */}\n        {/* accessibilityLayer adds ARIA labels for better screen reader support */}\n        \u003cBarChart accessibilityLayer data={popularRepos}\u003e\n          {/* CartesianGrid: Adds horizontal guide lines (vertical disabled) */}\n          \u003cCartesianGrid vertical={false} /\u003e\n\n          {/* XAxis: Horizontal axis showing repository names */}\n          {/* tickFormatter truncates long repository names to 10 characters */}\n          \u003cXAxis\n            dataKey=\"repo\"\n            tickLine={false}\n            tickMargin={10}\n            tickFormatter={(value) =\u003e value.slice(0, 10)}\n          /\u003e\n\n          {/* YAxis: Vertical axis showing star counts */}\n          \u003cYAxis dataKey=\"stars\" /\u003e\n\n          {/* ChartTooltip: Custom tooltip component that appears on hover */}\n          {/* ChartTooltipContent: Renders the actual content inside the tooltip */}\n          \u003cChartTooltip content={\u003cChartTooltipContent /\u003e} /\u003e\n\n          {/* Bar: The actual bar elements of the chart */}\n          {/* fill uses CSS variable for consistent theming */}\n          {/* radius adds rounded corners to the bars */}\n          \u003cBar dataKey=\"stars\" fill=\"var(--color-repo)\" radius={4} /\u003e\n        \u003c/BarChart\u003e\n      \u003c/ChartContainer\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default PopularRepos;\n```\n\n### Forked Repos\n\ncomponents/charts/ForkedRepos.tsx\n\n```tsx\nimport { type Repository } from \"@/types\";\nimport { Bar, BarChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n  ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport { calculateMostForkedRepos } from \"@/utils\";\n\nconst ForkedRepos = ({ repositories }: { repositories: Repository[] }) =\u003e {\n  // Calculate most forked repositories and return array of {repo: string, count: number}\n  const mostForkedRepos = calculateMostForkedRepos(repositories);\n\n  // Define chart configuration for styling and labels\n  const chartConfig = {\n    repo: {\n      label: \"Repository\",\n      color: \"#facd12\",\n    },\n  } satisfies ChartConfig;\n\n  return (\n    \u003cdiv\u003e\n      \u003ch2 className=\"text-2xl font-semibold text-center mb-4\"\u003eForked Repos\u003c/h2\u003e\n      {/* ChartContainer handles responsive sizing and theme variables */}\n      \u003cChartContainer config={chartConfig} className=\"h-100 w-full\"\u003e\n        {/* BarChart is the main container for the bar chart visualization */}\n        {/* accessibilityLayer adds ARIA labels for better screen reader support */}\n        \u003cBarChart accessibilityLayer data={mostForkedRepos}\u003e\n          {/* CartesianGrid adds background gridlines, vertical lines disabled */}\n          \u003cCartesianGrid vertical={false} /\u003e\n\n          {/* XAxis configures the horizontal axis */}\n          \u003cXAxis\n            dataKey=\"repo\" // Uses 'repo' property from data for labels\n            tickLine={true} // Shows small lines at each tick mark\n            tickMargin={10} // Space between tick line and label\n            axisLine={false} // Hides the main axis line\n            tickFormatter={(value) =\u003e value.slice(0, 10)} // Truncates long repo names\n          /\u003e\n\n          {/* YAxis configures the vertical axis, showing fork counts */}\n          \u003cYAxis dataKey=\"count\" /\u003e\n\n          {/* ChartTooltip shows details when hovering over bars */}\n          \u003cChartTooltip content={\u003cChartTooltipContent /\u003e} /\u003e\n\n          {/* Bar component defines the actual bars in the chart */}\n          {/* Uses CSS variable for color and rounded corners (radius) */}\n          \u003cBar dataKey=\"count\" fill=\"var(--color-repo)\" radius={4} /\u003e\n        \u003c/BarChart\u003e\n      \u003c/ChartContainer\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default ForkedRepos;\n```\n\n### Loading\n\nsrc/components/user/Loading.tsx\n\n```tsx\nimport { Skeleton } from \"@/components/ui/skeleton\";\n\n/**\n * Loading component that displays placeholder content while data is being fetched\n * Uses shadcn/ui's Skeleton component to create loading animations\n */\nconst Loading = () =\u003e {\n  return (\n    \u003cdiv\u003e\n      {/* Large header skeleton\n          - h-[194px]: Fixed height of 194px\n          - w-full: Full width on mobile\n          - lg:w-1/2: Half width on large screens\n          - mb-8: Bottom margin of 2rem */}\n      \u003cSkeleton className=\"h-[194px] w-full lg:w-1/2 mb-8 rounded \" /\u003e\n\n      {/* Grid container for smaller skeletons\n          - grid-cols-1: Single column on mobile\n          - lg:grid-cols-2: 2 columns on large screens\n          - xl:grid-cols-4: 4 columns on extra large screens\n          - gap-2: Small gap between grid items */}\n      \u003cdiv className=\"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-2 mb-8\"\u003e\n        {/* Four identical skeleton items\n            - h-[70px]: Fixed height of 70px\n            - rounded: Rounded corners */}\n        \u003cSkeleton className=\" h-[70px] rounded\" /\u003e\n        \u003cSkeleton className=\" h-[70px] rounded\" /\u003e\n        \u003cSkeleton className=\" h-[70px] rounded\" /\u003e\n        \u003cSkeleton className=\" h-[70px] rounded\" /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default Loading;\n```\n\nUserProfile.tsx\n\n```tsx\nif (loading) return \u003cLoading /\u003e;\n```\n\n### Automated Testing\n\nAdd RTL, Vitest and MSW to the project, please reference corresponding course sections.\n\n- 📁 Create a new directory named `src/__tests__`\n\n### Utils Test Challenge\n\n- Create a new file named `utils.ts` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import Repository type from '../types'\n  - Import utility functions (calculateMostForkedRepos, calculateMostStarredRepos, calculatePopularLanguages) from '../utils'\n\n- Create mock data (mockRepositories array):\n\n  - Create 3 repository objects with different values\n  - Each repo should have: name, description, stargazerCount, forkCount, url\n  - Include languages object with edges array containing language name and size\n  - Ensure variety in star counts, fork counts, and languages for testing\n\n- Create main describe block for 'repository statistics calculations'\n\n- Create calculateMostForkedRepos test suite:\n\n  - Test empty input returns empty array\n  - Test correct ranking of repositories by fork count\n  - Verify descending order of results\n\n- Create calculateMostStarredRepos test suite:\n\n  - Test empty input returns empty array\n  - Test correct ranking of repositories by star count\n  - Verify descending order of results\n\n- Create calculatePopularLanguages test suite:\n\n  - Test empty input returns empty array\n  - Test repositories with no languages\n  - Test language counting and ranking\n  - Verify accurate language occurrence counting\n\n- Add detailed comments throughout:\n  - Document mock data structure\n  - Explain purpose of each test suite\n  - Label edge cases and main functionality tests\n\n### Utils Test\n\n`src/__tests__/utils.ts`\n\n```tsx\n// Import the Repository type and utility functions being tested\nimport { Repository } from \"../types\";\nimport {\n  calculateMostForkedRepos,\n  calculateMostStarredRepos,\n  calculatePopularLanguages,\n} from \"../utils\";\n\n// Mock data representing a sample array of repository objects\n// Each repository contains basic info like name, description, stars, forks,\n// and a nested languages object with size information\nexport const mockRepositories: Repository[] = [\n  {\n    name: \"repo1\",\n    description: \"test repo 1\",\n    stargazerCount: 1000,\n    forkCount: 500,\n    url: \"https://github.com/test/repo1\",\n    languages: {\n      edges: [\n        { node: { name: \"javascript\" }, size: 1000 },\n        { node: { name: \"typescript\" }, size: 500 },\n      ],\n    },\n  },\n  {\n    name: \"repo2\",\n    description: \"test repo 2\",\n    stargazerCount: 2000,\n    forkCount: 300,\n    url: \"https://github.com/test/repo2\",\n    languages: {\n      edges: [\n        { node: { name: \"python\" }, size: 800 },\n        { node: { name: \"javascript\" }, size: 400 },\n      ],\n    },\n  },\n  {\n    name: \"repo3\",\n    description: \"test repo 3\",\n    stargazerCount: 3000,\n    forkCount: 1000,\n    url: \"https://github.com/test/repo3\",\n    languages: {\n      edges: [\n        { node: { name: \"typescript\" }, size: 1200 },\n        { node: { name: \"python\" }, size: 300 },\n      ],\n    },\n  },\n];\n\ndescribe(\"repository statistics calculations\", () =\u003e {\n  // Test suite for calculateMostForkedRepos function\n  describe(\"calculateMostForkedRepos\", () =\u003e {\n    // Edge case: Test behavior with empty input\n    test(\"should return empty array for empty input\", () =\u003e {\n      const result = calculateMostForkedRepos([]);\n      expect(result).toEqual([]);\n    });\n\n    // Main functionality test: Verify correct ranking of repositories by fork count\n    test(\"should return top 5 most forked repositories\", () =\u003e {\n      const result = calculateMostForkedRepos(mockRepositories);\n      expect(result).toEqual([\n        { repo: \"repo3\", count: 1000 },\n        { repo: \"repo1\", count: 500 },\n        { repo: \"repo2\", count: 300 },\n      ]);\n    });\n\n    // Verification test: Ensure proper descending order of fork counts\n    test(\"should sort repositories by fork count in descending order\", () =\u003e {\n      const result = calculateMostForkedRepos(mockRepositories);\n      expect(result[0].count).toBeGreaterThanOrEqual(result[1].count);\n      expect(result[1].count).toBeGreaterThanOrEqual(result[2].count);\n    });\n  });\n\n  // Test suite for calculateMostStarredRepos function\n  describe(\"calculateMostStarredRepos\", () =\u003e {\n    // Edge case: Test behavior with empty input\n    test(\"should return empty array for empty input\", () =\u003e {\n      const result = calculateMostStarredRepos([]);\n      expect(result).toEqual([]);\n    });\n\n    // Main functionality test: Verify correct ranking of repositories by star count\n    test(\"should return top 5 most starred repositories\", () =\u003e {\n      const result = calculateMostStarredRepos(mockRepositories);\n      expect(result).toEqual([\n        { repo: \"repo3\", stars: 3000 },\n        { repo: \"repo2\", stars: 2000 },\n        { repo: \"repo1\", stars: 1000 },\n      ]);\n    });\n\n    // Verification test: Ensure proper descending order of star counts\n    test(\"should sort repositories by star count in descending order\", () =\u003e {\n      const result = calculateMostStarredRepos(mockRepositories);\n      expect(result[0].stars).toBeGreaterThanOrEqual(result[1].stars);\n      expect(result[1].stars).toBeGreaterThanOrEqual(result[2].stars);\n    });\n  });\n\n  // Test suite for calculatePopularLanguages function\n  describe(\"calculatePopularLanguages\", () =\u003e {\n    // Edge case: Test empty input\n    test(\"should return empty array for empty input\", () =\u003e {\n      const result = calculatePopularLanguages([]);\n      expect(result).toEqual([]);\n    });\n\n    // Edge case: Test repositories with no languages\n    test(\"should return empty array when no languages are present\", () =\u003e {\n      const repoWithNoLanguages: Repository[] = [\n        {\n          ...mockRepositories[0],\n          languages: { edges: [] },\n        },\n      ];\n      const result = calculatePopularLanguages(repoWithNoLanguages);\n      expect(result).toEqual([]);\n    });\n\n    // Main functionality test: Verify language counting and ranking\n    test(\"should return top 5 most used languages\", () =\u003e {\n      const result = calculatePopularLanguages(mockRepositories);\n      expect(result).toEqual([\n        { language: \"javascript\", count: 2 },\n        { language: \"typescript\", count: 2 },\n        { language: \"python\", count: 2 },\n      ]);\n    });\n\n    // Specific test for accuracy of language occurrence counting\n    test(\"should count language occurrences correctly\", () =\u003e {\n      const result = calculatePopularLanguages(mockRepositories);\n      const jsCount = result.find(\n        (lang) =\u003e lang.language === \"javascript\"\n      )?.count;\n      expect(jsCount).toBe(2);\n    });\n  });\n});\n```\n\n### Stats Card Test Challenge\n\n- Create a new file named `StatsCard.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - `import { render, screen } from '@testing-library/react'`\n  - `import StatsCard from '@/components/user/StatsCard'`;\n\n- 🧪 Write test cases:\n\n  1. Create a test for basic rendering:\n\n     - Render StatsCard with a title \"Total Users\" and count 42\n     - Verify both text elements are in the document\n\n  2. Create a test for zero values:\n\n     - Render StatsCard with a title \"Active Sessions\" and count 0\n     - Verify both text elements are in the document\n\n  3. Create a test for large numbers:\n     - Render StatsCard with a title \"Total Views\" and count 1000000\n     - Verify both text elements are in the document\n\n### Stats Card Test\n\n`src/__tests__/StatsCard.test.tsx`\n\n```tsx\nimport { render, screen } from \"@testing-library/react\";\nimport StatsCard from \"@/components/user/StatsCard\";\n\ndescribe(\"StatsCard\", () =\u003e {\n  test(\"renders title and count correctly\", () =\u003e {\n    render(\u003cStatsCard title=\"Total Users\" count={42} /\u003e);\n\n    expect(screen.getByText(\"Total Users\")).toBeInTheDocument();\n    expect(screen.getByText(\"42\")).toBeInTheDocument();\n  });\n\n  test(\"renders with zero count\", () =\u003e {\n    render(\u003cStatsCard title=\"Active Sessions\" count={0} /\u003e);\n\n    expect(screen.getByText(\"Active Sessions\")).toBeInTheDocument();\n    expect(screen.getByText(\"0\")).toBeInTheDocument();\n  });\n\n  test(\"renders with large numbers\", () =\u003e {\n    render(\u003cStatsCard title=\"Total Views\" count={1000000} /\u003e);\n\n    expect(screen.getByText(\"Total Views\")).toBeInTheDocument();\n    expect(screen.getByText(\"1000000\")).toBeInTheDocument();\n  });\n});\n```\n\n### Stats Container Test Challenge\n\n- Create a new file named `StatsContainer.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import StatsContainer component from '@/components/user/StatsContainer'\n\n- Create main describe block for 'StatsContainer'\n\n- Create mock data for GitHub statistics:\n\n  - totalRepos: 25\n  - followers: 100\n  - following: 50\n  - gists: 10\n\n- Create test case for rendering stats cards:\n\n  - Test name: 'renders all stats cards with correct values'\n  - Render StatsContainer with mock props\n  - Verify repositories card:\n    - Check for 'Total Repositories' text\n    - Check for '25' value\n  - Verify followers card:\n    - Check for 'Followers' text\n    - Check for '100' value\n  - Verify following card:\n    - Check for 'Following' text\n    - Check for '50' value\n  - Verify gists card:\n    - Check for 'Gists' text\n    - Check for '10' value\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock data structure\n  - Explain what each verification checks\n\n### Stats Container Test\n\n`src/__tests__/StatsContainer.test.tsx`\n\n```tsx\n// This test file contains unit tests for the StatsContainer component\n// It verifies that the container correctly displays multiple StatsCard components\n// with their respective GitHub statistics\n\nimport { render, screen } from \"@testing-library/react\";\nimport StatsContainer from \"@/components/user/StatsContainer\";\n\ndescribe(\"StatsContainer\", () =\u003e {\n  // Test case: Verify all stats cards are rendered with their correct values\n  test(\"renders all stats cards with correct values\", () =\u003e {\n    // Mock data representing a GitHub user's statistics\n    const props = {\n      totalRepos: 25,\n      followers: 100,\n      following: 50,\n      gists: 10,\n    };\n\n    render(\u003cStatsContainer {...props} /\u003e);\n\n    // Verify the repositories card displays correctly\n    expect(screen.getByText(\"Total Repositories\")).toBeInTheDocument();\n    expect(screen.getByText(\"25\")).toBeInTheDocument();\n\n    // Verify the followers card displays correctly\n    expect(screen.getByText(\"Followers\")).toBeInTheDocument();\n    expect(screen.getByText(\"100\")).toBeInTheDocument();\n\n    // Verify the following card displays correctly\n    expect(screen.getByText(\"Following\")).toBeInTheDocument();\n    expect(screen.getByText(\"50\")).toBeInTheDocument();\n\n    // Verify the gists card displays correctly\n    expect(screen.getByText(\"Gists\")).toBeInTheDocument();\n    expect(screen.getByText(\"10\")).toBeInTheDocument();\n  });\n});\n```\n\n### User Card Test Challenge\n\n- Create a new file named `UserCard.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import UserCard component from '@/components/user/UserCard'\n\n- Create main describe block for 'UserCard'\n\n- Create mock data for user profile:\n\n  - avatarUrl: '\u003chttps://example.com/avatar.jpg\u003e'\n  - name: 'John Doe'\n  - bio: 'Frontend Developer'\n  - url: '\u003chttps://github.com/johndoe\u003e'\n\n- Create first test case for complete user information:\n\n  - Test name: 'renders user information correctly'\n  - Render UserCard with mock props\n  - Verify user name display\n  - Verify bio display\n  - Verify avatar image:\n    - Check presence in document\n    - Check src attribute\n    - Check alt attribute\n  - Verify follow button/link:\n\n    - Check href attribute\n    - Check target attribute\n    - Check rel attribute\n\n  - Create second test case for missing information:\n\n  - Test name: 'renders default values when name and bio are not provided'\n  - Create modified props with empty name and bio\n  - Render UserCard with modified props\n  - Verify default name display ('Coding Addict')\n  - Verify default bio display ('Passionate about coding and technology')\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock data structure\n  - Explain test cases and their purposes\n  - Document fallback behavior testing\n\n### User Card Test\n\n`src/__tests__/UserCard.test.tsx`\n\n```tsx\n// This test file contains unit tests for the UserCard component\n// It verifies the component's ability to display user profile information\n// and handle cases where some user data is missing\n\nimport { render, screen } from \"@testing-library/react\";\nimport UserCard from \"@/components/user/UserCard\";\n\ndescribe(\"UserCard\", () =\u003e {\n  // Mock data representing a typical GitHub user profile\n  const mockProps = {\n    avatarUrl: \"https://example.com/avatar.jpg\",\n    name: \"John Doe\",\n    bio: \"Frontend Developer\",\n    url: \"https://github.com/johndoe\",\n  };\n\n  // Test case: Verify all user information is displayed correctly\n  test(\"renders user information correctly\", () =\u003e {\n    render(\u003cUserCard {...mockProps} /\u003e);\n\n    // Verify user's name is displayed\n    expect(screen.getByText(\"John Doe\")).toBeInTheDocument();\n\n    // Verify user's bio is displayed\n    expect(screen.getByText(\"Frontend Developer\")).toBeInTheDocument();\n\n    // Verify avatar image is present with correct attributes\n    const avatarImage = screen.getByAltText(\"John Doe\");\n    expect(avatarImage).toBeInTheDocument();\n    expect(avatarImage).toHaveAttribute(\n      \"src\",\n      \"https://github.com/images/john_doe.jpg\"\n    );\n\n    // Verify follow button/link has correct attributes for external navigation\n    const followLink = screen.getByRole(\"link\", { name: /follow/i });\n    expect(followLink).toHaveAttribute(\"href\", \"https://github.com/johndoe\");\n    expect(followLink).toHaveAttribute(\"target\", \"_blank\");\n    expect(followLink).toHaveAttribute(\"rel\", \"noreferrer\");\n  });\n\n  // Test case: Verify fallback values when required fields are missing\n  test(\"renders default values when name and bio are not provided\", () =\u003e {\n    const propsWithoutNameAndBio = {\n      ...mockProps,\n      name: \"\",\n      bio: \"\",\n    };\n\n    render(\u003cUserCard {...propsWithoutNameAndBio} /\u003e);\n\n    // Verify default name is used when name is empty\n    expect(screen.getByText(\"Coding Addict\")).toBeInTheDocument();\n\n    // Verify default bio is used when bio is empty\n    expect(\n      screen.getByText(\"Passionate about coding and technology\")\n    ).toBeInTheDocument();\n  });\n});\n```\n\n### Search Form Test Challenge\n\n- Create a new file named `SearchForm.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import userEvent from '@testing-library/user-event'\n  - Import vi from 'vitest'\n  - Import SearchForm from '@/components/form/SearchForm'\n\n- Setup mocks:\n\n  - Create mockToast function\n  - Create setUserNameMock function\n  - Mock useToast hook to return mockToast\n\n- Create main describe block for 'SearchForm'\n\n- Setup test environment:\n\n  - Create userEvent instance\n  - Add beforeEach to clear mocks\n  - Create helper function getFormElements to return input and button\n\n- Create test cases:\n\n  - Test 'renders the search form correctly':\n\n    - Render form with username 'john_doe'\n    - Verify input value\n    - Verify button presence\n\n  - Test 'displays empty input when userName is empty':\n\n    - Render form with empty username\n    - Verify empty input value\n\n  - Test 'updates input value on change':\n\n    - Render form with empty username\n    - Type 'john_doe' in input\n    - Verify input value updated\n\n  - Test 'shows toast when submitting empty input':\n\n    - Render form with empty username\n    - Click submit button\n    - Verify toast called with error message\n    - Verify setUserName not called\n\n  - Test 'calls setUserName on valid form submission':\n    - Render form with empty username\n    - Type 'jane_doe' in input\n    - Click submit button\n    - Verify setUserName called with correct value\n    - Verify toast not called\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock setup\n  - Explain helper functions\n  - Document test cases and their purposes\n\n### Search Form Test\n\n`src/__tests__/SearchForm.test.tsx`\n\n```tsx\n// This test file contains unit tests for the SearchForm component\n// It tests form functionality, input validation, and error handling\n\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { vi } from \"vitest\";\nimport SearchForm from \"@/components/form/SearchForm\";\n\n// Mock dependencies and setup test doubles\nconst mockToast = vi.fn();\nconst setUserNameMock = vi.fn();\n\n// Mock the toast hook to test error notifications\nvi.mock(\"@/hooks/use-toast\", () =\u003e ({\n  useToast: () =\u003e ({\n    toast: mockToast,\n  }),\n}));\n\ndescribe(\"SearchForm\", () =\u003e {\n  const user = userEvent.setup();\n\n  // Reset all mocks before each test to ensure clean state\n  beforeEach(() =\u003e {\n    vi.clearAllMocks();\n  });\n\n  // Helper function to get form elements used across multiple tests\n  function getFormElements() {\n    const input = screen.getByRole(\"textbox\", { name: /search/i });\n    const button = screen.getByRole(\"button\", { name: /search/i });\n    return { input, button };\n  }\n\n  // Test case: Verify initial form rendering with provided username\n  test(\"renders the search form correctly\", () =\u003e {\n    render(\u003cSearchForm userName=\"john_doe\" setUserName={setUserNameMock} /\u003e);\n\n    const { input, button } = getFormElements();\n\n    expect(input).toHaveValue(\"john_doe\");\n    expect(button).toBeInTheDocument();\n  });\n\n  // Test case: Verify form handling of empty username\n  test(\"displays empty input when userName is empty\", () =\u003e {\n    render(\u003cSearchForm userName=\"\" setUserName={setUserNameMock} /\u003e);\n\n    const { input } = getFormElements();\n    expect(input).toHaveValue(\"\");\n  });\n\n  // Test case: Verify input change handling\n  test(\"updates input value on change\", async () =\u003e {\n    render(\u003cSearchForm userName=\"\" setUserName={setUserNameMock} /\u003e);\n\n    const { input } = getFormElements();\n\n    await user.type(input, \"john_doe\");\n    expect(input).toHaveValue(\"john_doe\");\n  });\n\n  // Test case: Verify error handling for empty submission\n  test(\"shows toast when submitting empty input\", async () =\u003e {\n    render(\u003cSearchForm userName=\"\" setUserName={setUserNameMock} /\u003e);\n\n    const { button } = getFormElements();\n    await user.click(button);\n    expect(mockToast).toHaveBeenCalledWith({\n      description: \"Please enter a valid username\",\n    });\n    expect(setUserNameMock).not.toHaveBeenCalled();\n  });\n\n  // Test case: Verify successful form submission\n  test(\"calls setUserName on valid form submission\", async () =\u003e {\n    render(\u003cSearchForm userName=\"\" setUserName={setUserNameMock} /\u003e);\n\n    const { input, button } = getFormElements();\n\n    await user.type(input, \"jane_doe\");\n    await user.click(button);\n\n    expect(setUserNameMock).toHaveBeenCalledWith(\"jane_doe\");\n    expect(mockToast).not.toHaveBeenCalled();\n  });\n});\n```\n\n### Forked Repos Test Challenge\n\n- Create a new file named `ForkedRepos.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import ForkedRepos from '@/components/charts/ForkedRepos'\n  - Import mockRepositories from './utils'\n\n- Setup mocks for UI components:\n\n  - Mock UsedLanguages: return static div\n  - Mock PopularRepos: return static div\n  - Mock ForkedRepos: return static div\n\n- Mock recharts components:\n\n  - BarChart: div wrapper\n  - CartesianGrid: static div\n  - XAxis: static div\n  - YAxis: static div\n  - Bar: static div\n\n- Create main describe block for 'ForkedRepos'\n\n- Setup test environment:\n\n  - Add beforeEach to render ForkedRepos with mockRepositories\n\n- Create test cases:\n\n  - Test 'should render the ForkedRepos component':\n\n    - Verify presence of 'Forked Repos' heading\n\n  - Test 'should render the chart with correct data':\n    - Verify presence of CartesianGrid\n    - Verify presence of XAxis\n    - Verify presence of YAxis\n    - Verify presence of Bar\n    - Verify presence of Tooltip Content\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock setup for UI components\n  - Document mock setup for recharts\n  - Explain test cases and their purposes\n\n### Forked Repos Test\n\n`src/__tests__/ForkedRepos.test.tsx`\n\n```tsx\n// This test file contains unit tests for the ForkedRepos component\n// It verifies the correct rendering of the chart component and its data visualization\n\nimport { render, screen } from \"@testing-library/react\";\nimport ForkedRepos from \"@/components/charts/ForkedRepos\";\nimport { mockRepositories } from \"./utils\";\n\n// Mock the chart UI components to simplify testing\n// Replace complex chart containers with simple div elements\nvi.mock(\"@/components/ui/chart\", () =\u003e {\n  return {\n    ChartContainer: ({ children }: { children: React.ReactNode }) =\u003e (\n      \u003cdiv\u003e{children}\u003c/div\u003e\n    ),\n    ChartTooltip: ({ content }: { content: React.ReactNode }) =\u003e (\n      \u003cdiv\u003e{content}\u003c/div\u003e\n    ),\n    ChartTooltipContent: () =\u003e \u003cdiv\u003eTooltip Content\u003c/div\u003e,\n  };\n});\n\n// Mock the recharts library components\n// Replace actual chart elements with simple div elements for testing\nvi.mock(\"recharts\", () =\u003e {\n  return {\n    BarChart: ({ children }: { children: React.ReactNode }) =\u003e (\n      \u003cdiv\u003e{children}\u003c/div\u003e\n    ),\n    CartesianGrid: () =\u003e \u003cdiv\u003eCartesianGrid\u003c/div\u003e,\n    XAxis: () =\u003e \u003cdiv\u003eXAxis\u003c/div\u003e,\n    YAxis: () =\u003e \u003cdiv\u003eYAxis\u003c/div\u003e,\n    Bar: () =\u003e \u003cdiv\u003eBar\u003c/div\u003e,\n  };\n});\n\ndescribe(\"ForkedRepos\", () =\u003e {\n  // Set up the component before each test\n  beforeEach(() =\u003e {\n    render(\u003cForkedRepos repositories={mockRepositories} /\u003e);\n  });\n\n  // Test case: Verify basic component rendering\n  test(\"should render the ForkedRepos component\", () =\u003e {\n    expect(screen.getByText(\"Forked Repos\")).toBeInTheDocument();\n  });\n\n  // Test case: Verify that all chart elements are present\n  test(\"should render the chart with correct data\", () =\u003e {\n    // Check for the presence of each chart element\n    expect(screen.getByText(\"CartesianGrid\")).toBeInTheDocument();\n    expect(screen.getByText(\"XAxis\")).toBeInTheDocument();\n    expect(screen.getByText(\"YAxis\")).toBeInTheDocument();\n    expect(screen.getByText(\"Bar\")).toBeInTheDocument();\n    expect(screen.getByText(\"Tooltip Content\")).toBeInTheDocument();\n  });\n});\n```\n\n### Handlers\n\n`src/mocks/handlers`\n\n```ts\nimport { graphql, HttpResponse } from \"msw\";\nimport { mockRepositories } from \"@/__tests__/utils\";\n\nexport const handlers = [\n  graphql.query(\"GetUser\", ({ query, variables }) =\u003e {\n    console.log(\"Intercepted GetUser GraphQL query:\", query);\n    const { login } = variables;\n    if (login === \"request-error\") {\n      return HttpResponse.json({\n        errors: [{ message: \"there was an error\" }],\n      });\n    }\n    if (login === \"invalid-username\") {\n      return HttpResponse.json({\n        data: {\n          user: null,\n        },\n        errors: [\n          {\n            message: `Could not resolve to a User with the login of ${login}.`,\n          },\n        ],\n      });\n    }\n    return HttpResponse.json({\n      data: {\n        user: {\n          name: login,\n          avatarUrl: `https://github.com/images/${login}.jpg`,\n          bio: \"Full-stack developer passionate about open source\",\n          url: `https://github.com/${login}`,\n          repositories: {\n            totalCount: 45,\n            nodes: mockRepositories,\n          },\n          followers: {\n            totalCount: 234,\n          },\n          following: {\n            totalCount: 156,\n          },\n          gists: {\n            totalCount: 27,\n          },\n        },\n      },\n    });\n  }),\n];\n```\n\n### UserProfile Test Challenge\n\n- Create a new file named `UserProfile.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import UserProfile from '@/components/user/UserProfile'\n  - Import client from '@/apolloClient'\n  - Import ApolloProvider from '@apollo/client'\n\n- Setup mocks for chart components:\n\n  - Mock UsedLanguages: return static div\n  - Mock PopularRepos: return static div\n  - Mock ForkedRepos: return static div\n\n- Create helper function `renderUserProfile`:\n\n  - Render UserProfile wrapped in ApolloProvider\n  - Pass userName as prop\n\n- Create main describe block for 'UserProfile'\n\n- Create test cases:\n\n  - Test 'renders UserProfile component':\n\n    - Use valid userName 'john_doe'\n    - Verify username display\n    - Verify avatar image with correct src\n    - Verify user bio display\n    - Verify GitHub profile link\n\n  - Test 'renders error message when request fails':\n\n    - Use userName 'request-error'\n    - Verify error message display\n\n  - Test 'renders error message when user not found':\n    - Use userName 'invalid-username'\n    - Verify user not found message display\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock setup for chart components\n  - Explain helper function purpose\n  - Document test cases and their purposes\n\n### UserProfile Test\n\n`src/__tests__/UserProfile.test.tsx`\n\n```tsx\n// This test file contains integration tests for the UserProfile component\n// It tests the component's ability to fetch and display user data using GraphQL,\n// as well as proper error handling for various scenarios\n\nimport { render, screen } from \"@testing-library/react\";\nimport UserProfile from \"@/components/user/UserProfile\";\nimport client from \"@/apolloClient\";\nimport { ApolloProvider } from \"@apollo/client\";\n\n// Mock chart components to simplify testing\n// Replace complex chart components with simple div elements\nvi.mock(\"@/components/charts/UsedLanguages\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003eUsed Languages\u003c/div\u003e,\n}));\n\nvi.mock(\"@/components/charts/PopularRepos\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003ePopular Repos\u003c/div\u003e,\n}));\n\nvi.mock(\"@/components/charts/ForkedRepos\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003eForked Repos\u003c/div\u003e,\n}));\n\n// Helper function to render the UserProfile component with Apollo Provider\n// This ensures GraphQL queries work correctly in tests\nconst renderUserProfile = async (userName: string) =\u003e {\n  render(\n    \u003cApolloProvider client={client}\u003e\n      \u003cUserProfile userName={userName} /\u003e\n    \u003c/ApolloProvider\u003e\n  );\n};\n\ndescribe(\"UserProfile\", () =\u003e {\n  // Test case: Verify successful profile rendering with valid user data\n  test(\"renders UserProfile component\", async () =\u003e {\n    const userName = \"john_doe\";\n    await renderUserProfile(userName);\n\n    // Verify username is displayed\n    expect(await screen.findByText(userName)).toBeInTheDocument();\n    expect(await screen.findByText(userName)).toBeInTheDocument();\n\n    // Verify avatar image is present with correct URL\n    expect(await screen.findByRole(\"img\")).toHaveAttribute(\n      \"src\",\n      `https://github.com/images/${userName}.jpg`\n    );\n\n    // Verify user bio is displayed\n    expect(\n      await screen.findByText(/full-stack developer/i)\n    ).toBeInTheDocument();\n\n    // Verify GitHub profile link is correct\n    expect(await screen.findByRole(\"link\")).toHaveAttribute(\n      \"href\",\n      `https://github.com/${userName}`\n    );\n  });\n\n  // Test case: Verify error handling for failed API requests\n  test(\"renders error message when request fails\", async () =\u003e {\n    const userName = \"request-error\";\n    await renderUserProfile(userName);\n    expect(await screen.findByText(\"there was an error\")).toBeInTheDocument();\n  });\n\n  // Test case: Verify error handling for non-existent users\n  test(\"renders error message when user not found\", async () =\u003e {\n    const userName = \"invalid-username\";\n    await renderUserProfile(userName);\n    expect(\n      await screen.findByText(/could not resolve to a user/i)\n    ).toBeInTheDocument();\n  });\n});\n```\n\n### App Test Challenge\n\n- Create a new file named `App.test.tsx` in the `__tests__` directory\n\n- Add required imports:\n\n  - Import render and screen from '@testing-library/react'\n  - Import userEvent from '@testing-library/user-event'\n  - Import ApolloProvider from '@apollo/client'\n  - Import client from '@/apolloClient'\n  - Import App from '@/App'\n\n- Setup mocks for chart components:\n\n  - Mock UsedLanguages: return static div\n  - Mock PopularRepos: return static div\n  - Mock ForkedRepos: return static div\n\n- Create helper function `renderApp`:\n\n  - Render App component wrapped in ApolloProvider\n\n- Create main describe block for 'App Integration'\n\n- Create test cases:\n\n  - Test 'should update profile when searching for a user':\n\n    - Setup userEvent\n    - Verify default user display\n    - Find search input\n    - Clear and type new username\n    - Submit form\n    - Verify new user info display\n    - Check avatar src\n    - Check profile link href\n\n  - Test 'should show error for invalid username':\n\n    - Setup userEvent\n    - Clear search input\n    - Type invalid username\n    - Submit form\n    - Verify error message display\n\n  - Test 'should show error when request fails':\n    - Setup userEvent\n    - Clear search input\n    - Type username causing request error\n    - Submit form\n    - Verify generic error message display\n\n- Add comments for documentation:\n  - Explain purpose of test file\n  - Document mock setup for chart components\n  - Explain helper function purpose\n  - Document test scenarios and expected behaviors\n\n### App Test\n\n`src/__tests__/App.test.tsx`\n\n```tsx\n// This test file contains integration tests for the main App component\n// It tests the core functionality of the application, including user search and error handling\n\nimport { render, screen } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\nimport { ApolloProvider } from \"@apollo/client\";\nimport client from \"@/apolloClient\";\nimport App from \"@/App\";\n\n// Mock the chart components to avoid error when rendering in simulated browser environment\n// Instead of rendering actual charts, we render simple div elements\nvi.mock(\"@/components/charts/UsedLanguages\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003eUsed Languages\u003c/div\u003e,\n}));\n\nvi.mock(\"@/components/charts/PopularRepos\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003ePopular Repos\u003c/div\u003e,\n}));\n\nvi.mock(\"@/components/charts/ForkedRepos\", () =\u003e ({\n  default: () =\u003e \u003cdiv\u003eForked Repos\u003c/div\u003e,\n}));\n\n// Helper function to render the App component wrapped with ApolloProvider\n// This setup is required for GraphQL functionality\nconst renderApp = () =\u003e {\n  render(\n    \u003cApolloProvider client={client}\u003e\n      \u003cApp /\u003e\n    \u003c/ApolloProvider\u003e\n  );\n};\n\ndescribe(\"App Integration\", () =\u003e {\n  // Test case: Verify that the profile updates when searching for a new user\n  test(\"should update profile when searching for a user\", async () =\u003e {\n    const user = userEvent.setup();\n    renderApp();\n\n    // Verify the default user is displayed initially\n    expect(await screen.findByText(\"quincylarson\")).toBeInTheDocument();\n\n    // Find the search input field\n    const searchInput = screen.getByRole(\"textbox\");\n\n    // Simulate user interaction: clear the input and type a new username\n    await user.clear(searchInput);\n    await user.type(searchInput, \"john_doe\");\n\n    // Simulate form submission\n    const submitButton = screen.getByRole(\"button\", { name: /search/i });\n    await user.click(submitButton);\n\n    // Verify that the new user's information is displayed\n    expect(await screen.findByText(\"john_doe\")).toBeInTheDocument();\n\n    // Verify that the user's avatar and profile link are updated correctly\n    expect(await screen.findByRole(\"img\")).toHaveAttribute(\n      \"src\",\n      \"https://github.com/images/john_doe.jpg\"\n    );\n    expect(await screen.findByRole(\"link\")).toHaveAttribute(\n      \"href\",\n      \"https://github.com/john_doe\"\n    );\n  });\n\n  // Test case: Verify error handling for invalid usernames\n  test(\"should show error for invalid username\", async () =\u003e {\n    const user = userEvent.setup();\n    renderApp();\n\n    // Simulate searching for an invalid username\n    const searchInput = screen.getByRole(\"textbox\");\n    await user.clear(searchInput);\n    await user.type(searchInput, \"invalid-username\");\n\n    const submitButton = screen.getByRole(\"button\", { name: /search/i });\n    await user.click(submitButton);\n\n    // Verify that the appropriate error message is displayed\n    expect(\n      await screen.findByText(/could not resolve to a user/i)\n    ).toBeInTheDocument();\n  });\n\n  // Test case: Verify error handling for failed API requests\n  test(\"should show error when request fails\", async () =\u003e {\n    const user = userEvent.setup();\n    renderApp();\n\n    // Simulate a failed request scenario\n    const searchInput = screen.getByRole(\"textbox\");\n    await user.clear(searchInput);\n    await user.type(searchInput, \"request-error\");\n\n    const submitButton = screen.getByRole(\"button\", { name: /search/i });\n    await user.click(submitButton);\n\n    // Verify that the generic error message is displayed\n    expect(await screen.findByText(\"there was an error\")).toBeInTheDocument();\n  });\n});\n```\n\n## Keywords\n\nReact, Vite, TypeScript, Tailwind CSS, shadcn/ui, Apollo Client, GraphQL, GitHub API, React Testing Library, Vitest, MSW, automated testing, charts, data visualization, user profile, repository statistics\n\n---\n\n## Conclusion\n\nThis project demonstrates a modern, fully tested React application with real-world API integration, advanced UI, and robust error handling. The included tests and code comments make it an excellent resource for learning about React, GraphQL, Apollo Client, and frontend testing best practices.\n\nFor more details, see the code comments and test files in the repository.\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farnobt78%2Fexplore-github-users-react-vitest-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farnobt78%2Fexplore-github-users-react-vitest-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farnobt78%2Fexplore-github-users-react-vitest-tutorial/lists"}