Skip to main content

Overview

Currently, Villa Buena E-Commerce does not have a dedicated testing framework configured. This page provides guidance on setting up testing and best practices for the project.
No test runner or testing libraries are currently installed in the project. This page serves as a guide for implementing testing in the future.

Test Framework Options

For a React + Vite project, consider these popular testing frameworks:

Vitest

Recommended - Native Vite integration, fast, Jest-compatible API
npm install -D vitest @vitest/ui

Jest

Industry standard, extensive ecosystem, requires additional configuration for Vite
npm install -D jest @types/jest

React Testing Library

For component testing, React Testing Library is the recommended approach:
npm install -D @testing-library/react @testing-library/jest-dom @testing-library/user-event

End-to-End Testing

For E2E testing, consider:
  • Playwright - Modern, multi-browser support
  • Cypress - Developer-friendly, extensive documentation
1

Install dependencies

npm install -D vitest @vitest/ui @testing-library/react @testing-library/jest-dom jsdom
2

Update vite.config.js

Add test configuration to your Vite config:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.js',
    css: true,
  },
})
3

Create test setup file

Create src/test/setup.js:
import { expect, afterEach } from 'vitest'
import { cleanup } from '@testing-library/react'
import * as matchers from '@testing-library/jest-dom/matchers'

expect.extend(matchers)

afterEach(() => {
  cleanup()
})
4

Add test scripts to package.json

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview",
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest --coverage"
  }
}

Test Structure

src/
├── components/
│   ├── ProductCard.jsx
│   └── ProductCard.test.jsx
├── services/
│   ├── api.js
│   └── api.test.js
├── hooks/
│   ├── useCart.js
│   └── useCart.test.js
├── test/
│   ├── setup.js
│   └── utils.js
└── __tests__/
    └── integration/
Co-locate test files with their corresponding components using the .test.jsx or .spec.jsx suffix.

Testing Best Practices

Component Testing

Example test for a React component:
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { ProductCard } from './ProductCard'

describe('ProductCard', () => {
  it('renders product name and price', () => {
    const product = {
      id: 1,
      name: 'Test Product',
      price: 29.99,
      image: '/test.jpg'
    }
    
    render(<ProductCard product={product} />)
    
    expect(screen.getByText('Test Product')).toBeInTheDocument()
    expect(screen.getByText('$29.99')).toBeInTheDocument()
  })
})

Testing with React Router

For components that use React Router (v7.13.1):
import { render } from '@testing-library/react'
import { createMemoryRouter, RouterProvider } from 'react-router-dom'
import { ProductPage } from './ProductPage'

const renderWithRouter = (component, { route = '/' } = {}) => {
  const router = createMemoryRouter(
    [{ path: route, element: component }],
    { initialEntries: [route] }
  )
  
  return render(<RouterProvider router={router} />)
}

it('renders product page', () => {
  renderWithRouter(<ProductPage />, { route: '/products/1' })
  // assertions...
})

Testing with Auth0

Mock the Auth0 provider for testing:
import { vi } from 'vitest'

vi.mock('@auth0/auth0-react', () => ({
  Auth0Provider: ({ children }) => children,
  useAuth0: () => ({
    isAuthenticated: true,
    user: { email: '[email protected]' },
    loginWithRedirect: vi.fn(),
    logout: vi.fn(),
  }),
}))

Testing TanStack Query

Wrap components with QueryClient for testing:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render } from '@testing-library/react'

const createTestQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: { retry: false },
    mutations: { retry: false },
  },
})

const renderWithQuery = (component) => {
  const queryClient = createTestQueryClient()
  return render(
    <QueryClientProvider client={queryClient}>
      {component}
    </QueryClientProvider>
  )
}

Testing Zustand Stores

Test Zustand stores in isolation:
import { renderHook, act } from '@testing-library/react'
import { useCartStore } from './cartStore'

it('adds item to cart', () => {
  const { result } = renderHook(() => useCartStore())
  
  act(() => {
    result.current.addItem({ id: 1, name: 'Product', price: 10 })
  })
  
  expect(result.current.items).toHaveLength(1)
  expect(result.current.items[0].id).toBe(1)
})

API Testing

Test the API service layer with mocked axios:
import { vi, describe, it, expect, beforeEach } from 'vitest'
import axios from 'axios'
import { api } from './api'

vi.mock('axios')

describe('API service', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })
  
  it('creates axios instance with correct baseURL', () => {
    expect(api.defaults.baseURL).toBe(import.meta.env.VITE_API_URL)
  })
})

Coverage Goals

Aim for meaningful coverage, not just high percentages. Focus on critical paths and business logic.
  • Statements: 80%+
  • Branches: 75%+
  • Functions: 80%+
  • Lines: 80%+

Generate Coverage Reports

npm run test:coverage
This generates a coverage report in the coverage/ directory.

Continuous Integration

GitHub Actions Example

Create .github/workflows/test.yml:
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run tests
        run: npm test
      
      - name: Generate coverage
        run: npm run test:coverage

Testing Checklist

1

Unit tests

Test individual functions and components in isolation
  • Utility functions
  • React hooks
  • Store logic
2

Component tests

Test components with React Testing Library
  • Rendering
  • User interactions
  • Props handling
  • Conditional rendering
3

Integration tests

Test component interactions and data flow
  • Forms with validation
  • API calls with TanStack Query
  • Router navigation
  • Authentication flows
4

E2E tests

Test critical user journeys
  • User registration/login
  • Product browsing and search
  • Add to cart and checkout
  • Order placement

Common Testing Patterns

Testing Async Operations

import { render, screen, waitFor } from '@testing-library/react'
import { ProductList } from './ProductList'

it('loads and displays products', async () => {
  render(<ProductList />)
  
  expect(screen.getByText('Loading...')).toBeInTheDocument()
  
  await waitFor(() => {
    expect(screen.getByText('Product 1')).toBeInTheDocument()
  })
})

Testing User Interactions

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { AddToCartButton } from './AddToCartButton'

it('calls onAdd when clicked', async () => {
  const user = userEvent.setup()
  const onAdd = vi.fn()
  
  render(<AddToCartButton onAdd={onAdd} />)
  
  await user.click(screen.getByRole('button', { name: /add to cart/i }))
  
  expect(onAdd).toHaveBeenCalledTimes(1)
})

Testing Forms

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'

it('submits form with user credentials', async () => {
  const user = userEvent.setup()
  const onSubmit = vi.fn()
  
  render(<LoginForm onSubmit={onSubmit} />)
  
  await user.type(screen.getByLabelText(/email/i), '[email protected]')
  await user.type(screen.getByLabelText(/password/i), 'password123')
  await user.click(screen.getByRole('button', { name: /login/i }))
  
  expect(onSubmit).toHaveBeenCalledWith({
    email: '[email protected]',
    password: 'password123'
  })
})

Next Steps

Setup

Configure your development environment

Building

Build the application for production
Start by adding tests for critical business logic and gradually increase coverage. Don’t aim for 100% coverage from day one.

Build docs developers (and LLMs) love