This document describes the testing setup and practices for the private-github-link project.
The project uses Vitest as the test runner with Testing Library for component testing and MSW (Mock Service Worker) for API mocking.
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Run tests with UI
npm run test:uiTests are co-located with their source files using the .test.ts or .test.tsx extension.
src/
├── lib/
│ ├── utils.ts
│ ├── utils.test.ts
│ ├── format.ts
│ ├── format.test.ts
│ └── github/
│ ├── api.ts
│ ├── api.test.ts
│ ├── client.ts
│ └── client.test.ts
├── components/
│ ├── error-fallback.tsx
│ ├── error-fallback.test.tsx
│ └── file-explorer/
│ ├── breadcrumb.tsx
│ ├── breadcrumb.test.tsx
│ └── viewers/
│ ├── code-viewer.tsx
│ ├── code-viewer.test.tsx
│ └── ...
└── test/
├── setup.ts # Test setup and global mocks
└── mocks/
├── handlers.ts # MSW request handlers
└── server.ts # MSW server configuration
Pure function tests without any mocking required.
utils.test.ts-cn()utility for className mergingformat.test.ts- Date and file size formattingtree-utils.test.ts- Tree traversal utilitiesfile-utils.test.ts- File type detection, MIME types, URL resolutionroute-utils.test.ts- Route parsing utilities
Tests for GitHub API client functions using MSW for request mocking.
client.test.ts-withTimeoututilityapi.test.ts- GitHub API functions (getContributors, getFileContent, etc.)
Note: Some API functions (getRepoInfo, getBranches, getRepoTree) use AbortSignal.timeout() which is incompatible with MSW in the jsdom test environment. These functions should be tested via integration tests.
React component tests using Testing Library.
- Simple Components:
error-fallback.test.tsx,breadcrumb.test.tsx - Theme Components:
theme-provider.test.tsx,mode-toggle.test.tsx - Viewer Components:
code-viewer.test.tsx,markdown-viewer.test.tsx,image-viewer.test.tsx, etc.
import { describe, expect, it } from "vitest";
import { myFunction } from "./my-module";
describe("myFunction", () => {
it("should do something", () => {
expect(myFunction(input)).toBe(expected);
});
});import { render, screen, fireEvent } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { MyComponent } from "./my-component";
describe("MyComponent", () => {
it("should render correctly", () => {
render(<MyComponent prop="value" />);
expect(screen.getByText("Expected Text")).toBeInTheDocument();
});
it("should handle click", () => {
const onClick = vi.fn();
render(<MyComponent onClick={onClick} />);
fireEvent.click(screen.getByRole("button"));
expect(onClick).toHaveBeenCalled();
});
});import { http, HttpResponse } from "msw";
import { server } from "@/test/mocks/server";
describe("API function", () => {
it("should handle error response", async () => {
// Override the default handler for this test
server.use(
http.get("https://api.github.com/endpoint", () => {
return HttpResponse.json({ message: "Error" }, { status: 500 });
})
);
await expect(apiFunction()).rejects.toThrow();
});
});import { renderHook, act } from "@testing-library/react";
import { useMyHook } from "./my-hook";
describe("useMyHook", () => {
it("should update state", () => {
const { result } = renderHook(() => useMyHook());
act(() => {
result.current.update("new value");
});
expect(result.current.value).toBe("new value");
});
});Default mock handlers are defined in src/test/mocks/handlers.ts. They provide successful responses for all GitHub API endpoints.
To test error cases, use server.use() to override handlers in individual tests:
server.use(
http.get("https://api.github.com/repos/:owner/:repo", () => {
return HttpResponse.json(
{ message: "Not Found" },
{ status: 404 }
);
})
);- Environment: jsdom
- Setup File:
src/test/setup.ts - Path Aliases:
@/maps tosrc/ - Coverage: V8 provider with HTML and LCOV reporters
The following are excluded from coverage:
node_modules/src/test/- Test utilitiessrc/components/ui/- ShadCN components (external library)src/routeTree.gen.ts- Generated file- Config files
- Build outputs
-
AbortSignal.timeout(): Functions using
AbortSignal.timeout()cannot be tested with MSW in jsdom due to a compatibility issue with Node's undici implementation. -
Lucide Icons: Icon class names from lucide-react are not predictable in tests. Use
document.querySelectorAll("svg")to verify icons are rendered. -
Dropdown Menus: Radix UI dropdown menus require clicking to open before testing menu items.
- Test behavior, not implementation - Focus on what the user sees and does
- Use Testing Library queries - Prefer
getByRole,getByText,getByLabelText - Mock at the boundary - Mock API calls with MSW, not internal functions
- Keep tests isolated - Each test should be independent
- Use descriptive test names -
it("should display error when API fails")
- Check for unhandled promises
- Verify MSW handlers are returning responses
- Look for infinite loops or effects
- Ensure the URL matches exactly (including query params)
- Check that the handler is registered in
handlers.ts - Verify
server.listen()is called in setup
- Check for missing providers (ThemeProvider, etc.)
- Verify all required props are passed
- Look for errors in the console output