Documentation Index
Fetch the complete documentation index at: https://devkit4ai.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
The Starter Kit includes comprehensive testing infrastructure using Vitest for integration tests and Playwright for end-to-end testing.
Test types
Integration tests (Vitest):
- Test utilities and helper functions
- Validate configuration logic
- Check data transformations
- Run fast without external dependencies
E2E tests (Playwright):
- Test complete user workflows
- Verify authentication flows
- Check page navigation
- Test form submissions
Running tests
All tests
Run both integration and E2E tests:
Integration tests only
Watch mode for development:
npm run test:integration:watch
E2E tests only
Run with UI mode:
Writing integration tests
Create tests in tests/integration/ or colocate with your code:
import { describe, it, expect } from 'vitest'
import { sanitizeReturnUrl } from '@/lib/return-url'
describe('sanitizeReturnUrl', () => {
it('allows valid relative paths', () => {
expect(sanitizeReturnUrl('/dashboard')).toBe('/dashboard')
expect(sanitizeReturnUrl('/profile')).toBe('/profile')
})
it('rejects absolute URLs', () => {
expect(sanitizeReturnUrl('https://evil.com')).toBe(null)
})
it('rejects protocol-relative URLs', () => {
expect(sanitizeReturnUrl('//evil.com/path')).toBe(null)
})
})
Testing Server Actions
Test server-side logic:
__tests__/actions.test.ts
import { describe, it, expect, vi } from 'vitest'
// Mock fetch for testing
global.fetch = vi.fn()
describe('Server Actions', () => {
it('handles API errors gracefully', async () => {
vi.mocked(fetch).mockResolvedValueOnce({
ok: false,
status: 401,
json: async () => ({ detail: 'Unauthorized' })
} as Response)
const result = await myServerAction()
expect(result.error).toBe('Unauthorized')
})
})
Writing E2E tests
Create tests in tests/e2e/:
import { test, expect } from '@playwright/test'
test.describe('Authentication', () => {
test('user can log in', async ({ page }) => {
await page.goto('/login')
// Fill login form
await page.fill('[name="email"]', 'test@example.com')
await page.fill('[name="password"]', 'password123')
// Submit form
await page.click('button[type="submit"]')
// Verify redirect to dashboard
await expect(page).toHaveURL('/dashboard')
// Verify user is logged in
await expect(page.locator('text=Welcome')).toBeVisible()
})
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('/login')
await page.fill('[name="email"]', 'wrong@example.com')
await page.fill('[name="password"]', 'wrongpassword')
await page.click('button[type="submit"]')
// Verify error message
await expect(page.locator('text=Invalid credentials')).toBeVisible()
})
})
Testing protected pages
tests/e2e/protected.spec.ts
import { test, expect } from '@playwright/test'
test('redirects to login when not authenticated', async ({ page }) => {
await page.goto('/dashboard')
// Should redirect to login
await expect(page).toHaveURL(/\/login/)
})
test('allows access when authenticated', async ({ page }) => {
// Login first
await page.goto('/login')
await page.fill('[name="email"]', 'test@example.com')
await page.fill('[name="password"]', 'password123')
await page.click('button[type="submit"]')
// Navigate to protected page
await page.goto('/dashboard')
// Should allow access
await expect(page).toHaveURL('/dashboard')
})
Test configuration
Vitest config
vitest.config.ts includes:
- TypeScript support
- Path aliases (@/ imports)
- Coverage reporting
- Fast watch mode
Playwright config
playwright.config.ts includes:
- Multiple browsers (Chromium, Firefox, WebKit)
- Mobile viewport testing
- Screenshot on failure
- Video recording for CI
CI/CD integration
Run tests in your CI pipeline:
.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: '19'
- run: npm install
- run: npm run test
- uses: actions/upload-artifact@v3
if: failure()
with:
name: playwright-report
path: playwright-report/
Best practices
- Test one thing per test case
- Use descriptive test names
- Group related tests with
describe()
Mock external dependencies
import { vi } from 'vitest'
// Mock Cloud API calls
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ id: '123', name: 'Test' })
})
Don’t just test happy paths:
- Invalid inputs
- Network failures
- Permission errors
- Edge cases
Create reusable test data:const mockUser = {
id: '123',
email: 'test@example.com',
role: 'end_user'
}
Troubleshooting
Common issues:
- Port conflicts: Ensure dev server isn’t running during E2E tests
- Flaky tests: Add proper wait conditions with Playwright
- Environment variables: Set test-specific values in test setup
Next steps
Deployment
Deploy with confidence after testing
Best Practices
Learn testing strategies