# Testing Strategy
## Testing Pyramid
```
E2E Tests (Manual)
/ \
Integration Tests (Backend)
/ \
Frontend Unit Tests Backend Unit Tests
```
**Coverage Targets:**
- Frontend: >70% for utils, services, business logic
- Backend: >80% for controllers, services, business logic
- Manual E2E: All critical user flows tested before deployment
## Test Organization
### Frontend Tests
```
frontend/tests/
├── components/
│ ├── BookCard.test.jsx
│ ├── LogProgressModal.test.jsx
│ └── StatusIndicator.test.jsx
├── utils/
│ ├── dateUtils.test.js
│ ├── paceUtils.test.js
│ └── validation.test.js
└── services/
└── booksService.test.js
```
### Backend Tests
```
backend/tests/
├── controllers/
│ ├── booksController.test.js
│ └── logsController.test.js
├── services/
│ ├── openLibraryService.test.js
│ └── paceCalculationService.test.js
└── integration/
└── api.test.js
```
### E2E Tests (Manual Checklist)
```
Manual E2E Test Checklist:
- [ ] Add book: Search → Select → Set deadline → Verify in list
- [ ] Log progress: Tap book → Enter page → Verify status updates
- [ ] View calendar: Navigate to book detail → See logged days
- [ ] Install PWA: "Add to Home Screen" → Launch from home screen
- [ ] Offline mode: Disable network → Log entry → Re-enable → Verify sync
- [ ] Mobile responsiveness: Test on iPhone and Android
```
## Test Examples
### Frontend Component Test
```typescript
// tests/components/StatusIndicator.test.jsx
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import StatusIndicator from '../../src/components/progress/StatusIndicator';
describe('StatusIndicator', () => {
it('displays green indicator for on-track status', () => {
render();
const indicator = screen.getByText(/on track/i);
expect(indicator).toBeInTheDocument();
expect(indicator).toHaveClass('text-green-600'); // Tailwind class
});
it('displays yellow indicator for slightly-behind status', () => {
render();
const indicator = screen.getByText(/slightly behind/i);
expect(indicator).toBeInTheDocument();
expect(indicator).toHaveClass('text-yellow-600');
});
it('displays red indicator for behind status', () => {
render();
const indicator = screen.getByText(/behind/i);
expect(indicator).toBeInTheDocument();
expect(indicator).toHaveClass('text-red-600');
});
});
```
### Backend API Test
```typescript
// tests/controllers/booksController.test.js
const request = require('supertest');
const app = require('../../src/server');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
describe('Books API', () => {
beforeAll(async () => {
// Setup test database
await prisma.book.deleteMany();
});
afterAll(async () => {
await prisma.$disconnect();
});
describe('POST /api/books', () => {
it('creates a new book with valid data', async () => {
const bookData = {
title: 'The Name of the Wind',
author: 'Patrick Rothfuss',
totalPages: 662,
deadlineDate: '2025-12-31',
};
const response = await request(app)
.post('/api/books')
.send(bookData)
.expect(201);
expect(response.body).toMatchObject({
title: bookData.title,
author: bookData.author,
totalPages: bookData.totalPages,
status: 'reading',
});
expect(response.body.id).toBeDefined();
});
it('returns 400 for invalid deadline (past date)', async () => {
const bookData = {
title: 'Test Book',
totalPages: 300,
deadlineDate: '2020-01-01', // Past date
};
const response = await request(app)
.post('/api/books')
.send(bookData)
.expect(400);
expect(response.body.error).toBe('Deadline must be in the future');
});
});
describe('GET /api/books', () => {
it('returns all active books with progress', async () => {
// Create test book
await prisma.book.create({
data: {
title: 'Test Book',
totalPages: 300,
deadlineDate: new Date('2025-12-31'),
},
});
const response = await request(app)
.get('/api/books')
.expect(200);
expect(response.body.books).toBeInstanceOf(Array);
expect(response.body.books.length).toBeGreaterThan(0);
expect(response.body.books[0]).toHaveProperty('requiredPace');
expect(response.body.books[0]).toHaveProperty('status');
});
});
});
```
### E2E Test (Manual)
**Test Case:** Add Book and Log Progress
**Steps:**
1. Navigate to http://localhost:5173
2. Click "Add Book" button
3. Search for "Name of the Wind"
4. Select first result
5. Set deadline to 30 days from today
6. Click "Add to Reading List"
7. Verify book appears in list with status "on-track" or "unknown"
8. Click "Log Progress" button
9. Enter page number: 50
10. Click "Save"
11. Verify status updates to show required pace and actual pace
**Expected Result:**
- Book added successfully
- Progress logged successfully
- Status indicator shows appropriate color
- Pace calculations display correctly
---