mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 22:04:10 +00:00
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>
202 lines
6.4 KiB
TypeScript
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)
|
|
}
|
|
})
|
|
})
|