Write comprehensive tests for React components with Testing Library in Google Antigravity including mocking and accessibility
# React Testing Library Patterns for Google Antigravity
Testing ensures code reliability and enables confident refactoring. This guide establishes testing patterns for React applications in Google Antigravity projects, enabling Gemini 3 to generate comprehensive, maintainable test suites.
## Test Setup Configuration
Configure the testing environment:
```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./src/test/setup.ts"],
include: ["**/*.{test,spec}.{ts,tsx}"],
coverage: {
provider: "v8",
reporter: ["text", "html", "lcov"],
exclude: ["node_modules", "src/test"],
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
// src/test/setup.ts
import "@testing-library/jest-dom";
import { cleanup } from "@testing-library/react";
import { afterEach, vi } from "vitest";
afterEach(() => {
cleanup();
});
// Mock next/navigation
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
back: vi.fn(),
prefetch: vi.fn(),
}),
useSearchParams: () => new URLSearchParams(),
usePathname: () => "/",
}));
```
## Component Testing
Test React components thoroughly:
```typescript
// components/LoginForm.test.tsx
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { LoginForm } from "./LoginForm";
const mockLogin = vi.fn();
vi.mock("@/hooks/useAuth", () => ({
useAuth: () => ({
login: mockLogin,
isLoading: false,
}),
}));
describe("LoginForm", () => {
beforeEach(() => {
mockLogin.mockReset();
});
it("renders email and password fields", () => {
render(<LoginForm />);
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
expect(screen.getByRole("button", { name: /sign in/i })).toBeInTheDocument();
});
it("shows validation errors for empty submission", async () => {
const user = userEvent.setup();
render(<LoginForm />);
await user.click(screen.getByRole("button", { name: /sign in/i }));
await waitFor(() => {
expect(screen.getByText(/email is required/i)).toBeInTheDocument();
expect(screen.getByText(/password is required/i)).toBeInTheDocument();
});
});
it("validates email format", async () => {
const user = userEvent.setup();
render(<LoginForm />);
await user.type(screen.getByLabelText(/email/i), "invalid-email");
await user.type(screen.getByLabelText(/password/i), "password123");
await user.click(screen.getByRole("button", { name: /sign in/i }));
await waitFor(() => {
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
});
});
it("submits form with valid data", async () => {
const user = userEvent.setup();
mockLogin.mockResolvedValue({ success: true });
render(<LoginForm />);
await user.type(screen.getByLabelText(/email/i), "test@example.com");
await user.type(screen.getByLabelText(/password/i), "password123");
await user.click(screen.getByRole("button", { name: /sign in/i }));
await waitFor(() => {
expect(mockLogin).toHaveBeenCalledWith({
email: "test@example.com",
password: "password123",
});
});
});
it("displays error message on failed login", async () => {
const user = userEvent.setup();
mockLogin.mockResolvedValue({ success: false, error: "Invalid credentials" });
render(<LoginForm />);
await user.type(screen.getByLabelText(/email/i), "test@example.com");
await user.type(screen.getByLabelText(/password/i), "wrongpassword");
await user.click(screen.getByRole("button", { name: /sign in/i }));
await waitFor(() => {
expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument();
});
});
});
```
## Custom Hook Testing
Test hooks in isolation:
```typescript
// hooks/useDebounce.test.ts
import { renderHook, act } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { useDebounce } from "./useDebounce";
describe("useDebounce", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it("returns initial value immediately", () => {
const { result } = renderHook(() => useDebounce("initial", 500));
expect(result.current).toBe("initial");
});
it("debounces value updates", () => {
const { result, rerender } = renderHook(
({ value }) => useDebounce(value, 500),
{ initialProps: { value: "initial" } }
);
rerender({ value: "updated" });
expect(result.current).toBe("initial");
act(() => {
vi.advanceTimersByTime(499);
});
expect(result.current).toBe("initial");
act(() => {
vi.advanceTimersByTime(1);
});
expect(result.current).toBe("updated");
});
it("cancels pending updates on rapid changes", () => {
const { result, rerender } = renderHook(
({ value }) => useDebounce(value, 500),
{ initialProps: { value: "a" } }
);
rerender({ value: "b" });
act(() => vi.advanceTimersByTime(200));
rerender({ value: "c" });
act(() => vi.advanceTimersByTime(200));
rerender({ value: "d" });
act(() => vi.advanceTimersByTime(500));
expect(result.current).toBe("d");
});
});
```
## Accessibility Testing
Verify accessibility compliance:
```typescript
// components/Modal.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { axe, toHaveNoViolations } from "jest-axe";
import { describe, it, expect } from "vitest";
import { Modal } from "./Modal";
expect.extend(toHaveNoViolations);
describe("Modal accessibility", () => {
it("has no accessibility violations", async () => {
const { container } = render(
<Modal isOpen={true} onClose={() => {}} title="Test Modal">
<p>Modal content</p>
</Modal>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it("traps focus within modal", async () => {
const user = userEvent.setup();
render(
<Modal isOpen={true} onClose={() => {}} title="Test Modal">
<button>First</button>
<button>Last</button>
</Modal>
);
await user.tab();
expect(screen.getByText("First")).toHaveFocus();
await user.tab();
expect(screen.getByText("Last")).toHaveFocus();
await user.tab();
expect(screen.getByRole("button", { name: /close/i })).toHaveFocus();
});
});
```
## Best Practices
1. **Query by role**: Prefer accessible queries over test IDs
2. **User-centric testing**: Test behavior, not implementation
3. **Async handling**: Use waitFor for async operations
4. **Isolation**: Mock external dependencies
5. **Accessibility**: Include a11y testing in your suiteThis Testing prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their testing implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Testing projects, consider mentioning your framework version, coding style, and any specific libraries you're using.