cms.c2sgmbh/tests/e2e/search.e2e.spec.ts
Martin Porwoll a88e4f60d0 test: add E2E and integration tests with documentation
Tests:
- Update frontend.e2e.spec.ts with locale testing
- Add search.e2e.spec.ts for search functionality
- Add i18n.int.spec.ts for localization tests
- Add search.int.spec.ts for search integration
- Update playwright.config.ts

Documentation:
- Add CLAUDE.md with project instructions
- Add docs/ directory with detailed documentation
- Add scripts/ for utility scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 08:19:52 +00:00

202 lines
6.4 KiB
TypeScript

import { test, expect } from '@playwright/test'
test.describe('Search API', () => {
test('GET /api/search returns valid response structure', async ({ request }) => {
const response = await request.get('/api/search?q=test')
expect(response.ok()).toBe(true)
const data = await response.json()
// Validate response structure
expect(data).toHaveProperty('results')
expect(data).toHaveProperty('total')
expect(data).toHaveProperty('query')
expect(data).toHaveProperty('filters')
expect(data).toHaveProperty('pagination')
expect(Array.isArray(data.results)).toBe(true)
expect(typeof data.total).toBe('number')
expect(data.query).toBe('test')
expect(data.pagination).toHaveProperty('limit')
expect(data.pagination).toHaveProperty('offset')
expect(data.pagination).toHaveProperty('hasMore')
})
test('GET /api/search validates minimum query length', async ({ request }) => {
const response = await request.get('/api/search?q=a')
expect(response.status()).toBe(400)
const data = await response.json()
expect(data).toHaveProperty('error')
expect(data.error).toContain('at least 2 characters')
})
test('GET /api/search validates maximum query length', async ({ request }) => {
const longQuery = 'a'.repeat(101)
const response = await request.get(`/api/search?q=${longQuery}`)
expect(response.status()).toBe(400)
const data = await response.json()
expect(data).toHaveProperty('error')
expect(data.error).toContain('at most 100 characters')
})
test('GET /api/search validates type parameter', async ({ request }) => {
const response = await request.get('/api/search?q=test&type=invalid')
expect(response.status()).toBe(400)
const data = await response.json()
expect(data).toHaveProperty('error')
expect(data.error).toContain('Invalid type')
})
test('GET /api/search respects limit parameter', async ({ request }) => {
const response = await request.get('/api/search?q=test&limit=5')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data.pagination.limit).toBe(5)
expect(data.results.length).toBeLessThanOrEqual(5)
})
test('GET /api/search includes rate limit headers', async ({ request }) => {
const response = await request.get('/api/search?q=test')
expect(response.headers()['x-ratelimit-remaining']).toBeDefined()
})
})
test.describe('Suggestions API', () => {
test('GET /api/search/suggestions returns valid response structure', async ({ request }) => {
const response = await request.get('/api/search/suggestions?q=test')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data).toHaveProperty('suggestions')
expect(Array.isArray(data.suggestions)).toBe(true)
})
test('GET /api/search/suggestions returns empty for short query', async ({ request }) => {
const response = await request.get('/api/search/suggestions?q=a')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data.suggestions).toEqual([])
})
test('GET /api/search/suggestions respects limit parameter', async ({ request }) => {
const response = await request.get('/api/search/suggestions?q=test&limit=3')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data.suggestions.length).toBeLessThanOrEqual(3)
})
test('GET /api/search/suggestions suggestion items have correct structure', async ({
request,
}) => {
const response = await request.get('/api/search/suggestions?q=test')
expect(response.ok()).toBe(true)
const data = await response.json()
for (const suggestion of data.suggestions) {
expect(suggestion).toHaveProperty('title')
expect(suggestion).toHaveProperty('slug')
expect(suggestion).toHaveProperty('type')
}
})
})
test.describe('Posts API', () => {
test('GET /api/posts returns valid response structure', async ({ request }) => {
const response = await request.get('/api/posts')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data).toHaveProperty('docs')
expect(data).toHaveProperty('pagination')
expect(data).toHaveProperty('filters')
expect(Array.isArray(data.docs)).toBe(true)
expect(data.pagination).toHaveProperty('page')
expect(data.pagination).toHaveProperty('limit')
expect(data.pagination).toHaveProperty('totalPages')
expect(data.pagination).toHaveProperty('totalDocs')
expect(data.pagination).toHaveProperty('hasNextPage')
expect(data.pagination).toHaveProperty('hasPrevPage')
})
test('GET /api/posts validates type parameter', async ({ request }) => {
const response = await request.get('/api/posts?type=invalid')
expect(response.status()).toBe(400)
const data = await response.json()
expect(data).toHaveProperty('error')
expect(data.error).toContain('Invalid type')
})
test('GET /api/posts respects pagination parameters', async ({ request }) => {
const response = await request.get('/api/posts?page=1&limit=5')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data.pagination.page).toBe(1)
expect(data.pagination.limit).toBe(5)
expect(data.docs.length).toBeLessThanOrEqual(5)
})
test('GET /api/posts filters by type', async ({ request }) => {
const response = await request.get('/api/posts?type=blog')
expect(response.ok()).toBe(true)
const data = await response.json()
expect(data.filters.type).toBe('blog')
// All returned posts should be of type blog
for (const post of data.docs) {
expect(post.type).toBe('blog')
}
})
test('GET /api/posts includes rate limit headers', async ({ request }) => {
const response = await request.get('/api/posts')
expect(response.headers()['x-ratelimit-remaining']).toBeDefined()
})
test('GET /api/posts doc items have correct structure', async ({ request }) => {
const response = await request.get('/api/posts')
expect(response.ok()).toBe(true)
const data = await response.json()
for (const post of data.docs) {
expect(post).toHaveProperty('id')
expect(post).toHaveProperty('title')
expect(post).toHaveProperty('slug')
expect(post).toHaveProperty('type')
// Optional fields
expect('excerpt' in post).toBe(true)
expect('publishedAt' in post).toBe(true)
expect('featuredImage' in post).toBe(true)
expect('category' in post).toBe(true)
}
})
})