import { test, expect } from "@playwright/test"; test.describe("Gravl API Tests", () => { const BASE_URL = process.env.STAGING_URL || "http://localhost:5173"; const API_URL = process.env.API_URL || "http://localhost:5173/api"; // ========== ORIGINAL TESTS (06-04) ========== test("homepage loads successfully", async ({ request }) => { const response = await request.get(`${BASE_URL}/`); expect(response.status()).toBe(200); const html = await response.text(); expect(html).toContain("Gravl"); }); test("login page is accessible", async ({ request }) => { const response = await request.get(`${BASE_URL}/login`); expect([200, 301, 302]).toContain(response.status()); }); test("API connectivity check", async ({ request }) => { // Check if backend API is accessible const response = await request.get(`${BASE_URL}/`); expect(response.status()).toBeLessThan(500); }); // ========== NEW TESTS: EXERCISE API ENDPOINTS (06-05) ========== // Test 4: GET /api/exercises - Fetch all exercises test("GET /api/exercises returns exercises list", async ({ request }) => { const response = await request.get(`${API_URL}/exercises`); expect(response.status()).toBe(200); const data = await response.json(); expect(Array.isArray(data)).toBeTruthy(); }); // Test 5: GET /api/exercises with pagination test("GET /api/exercises with limit and offset parameters", async ({ request }) => { const response = await request.get(`${API_URL}/exercises?limit=5&offset=0`); expect(response.status()).toBe(200); const data = await response.json(); expect(Array.isArray(data)).toBeTruthy(); expect(data.length).toBeLessThanOrEqual(5); }); // Test 6: GET /api/exercises - Search functionality test("GET /api/exercises with search query", async ({ request }) => { const response = await request.get(`${API_URL}/exercises?search=squat`); expect(response.status()).toBe(200); const data = await response.json(); expect(Array.isArray(data)).toBeTruthy(); }); // Test 7: GET /api/exercises - Filter by difficulty test("GET /api/exercises with difficulty filter", async ({ request }) => { const response = await request.get(`${API_URL}/exercises?difficulty=beginner`); expect(response.status()).toBe(200); const data = await response.json(); expect(Array.isArray(data)).toBeTruthy(); if (data.length > 0) { data.forEach((exercise) => { expect(["beginner", "intermediate", "advanced"]).toContain(exercise.difficulty); }); } }); // Test 8: GET /api/exercises/:id - Get non-existent exercise (404 error handling) test("GET /api/exercises/:id returns 404 for non-existent ID", async ({ request }) => { const response = await request.get(`${API_URL}/exercises/99999`); expect(response.status()).toBe(404); const data = await response.json(); expect(data.error).toContain("not found"); }); // ========== NEW TESTS: DATA VALIDATION ========== // Test 9: POST /api/exercises - Invalid payload (missing required fields) test("POST /api/exercises rejects invalid data - missing name", async ({ request }) => { const response = await request.post(`${API_URL}/exercises`, { data: { description: "A test exercise", difficulty: "intermediate" } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); expect(data.details).toBeDefined(); }); // Test 10: POST /api/exercises - Invalid difficulty value test("POST /api/exercises rejects invalid difficulty", async ({ request }) => { const response = await request.post(`${API_URL}/exercises`, { data: { name: "Test Exercise", difficulty: "invalid_level", muscle_groups: ["chest"], equipment_needed: [] } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); // Test 11: POST /api/exercises - Invalid array fields test("POST /api/exercises rejects non-array muscle_groups", async ({ request }) => { const response = await request.post(`${API_URL}/exercises`, { data: { name: "Test Exercise", difficulty: "beginner", muscle_groups: "not_an_array", equipment_needed: [] } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); // ========== NEW TESTS: EXERCISE RECOMMENDATIONS API ========== // Test 12: POST /api/exercises/recommend - Valid recommendation request test("POST /api/exercises/recommend returns recommendations", async ({ request }) => { const response = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "beginner", goals: ["strength", "hypertrophy"], available_time: 30 } }); expect([200, 400]).toContain(response.status()); if (response.status() === 200) { const data = await response.json(); expect(data.recommendations).toBeDefined(); expect(Array.isArray(data.recommendations)).toBeTruthy(); expect(data.status).toBeDefined(); } }); // Test 13: POST /api/exercises/recommend - Invalid fitness_level test("POST /api/exercises/recommend rejects invalid fitness_level", async ({ request }) => { const response = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "invalid_level", goals: ["strength"], available_time: 30 } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); // Test 14: POST /api/exercises/recommend - Missing goals test("POST /api/exercises/recommend rejects missing goals", async ({ request }) => { const response = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "intermediate", goals: [], available_time: 30 } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); // Test 15: POST /api/exercises/recommend - Invalid available_time test("POST /api/exercises/recommend rejects invalid available_time", async ({ request }) => { const response = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "advanced", goals: ["fat_loss"], available_time: -10 } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); // ========== NEW TESTS: FRONTEND INTEGRATION ========== // Test 16: Multiple API calls - Simulating user flow test("Frontend integration flow - exercises then recommendations", async ({ request }) => { const exercisesResponse = await request.get(`${API_URL}/exercises?limit=3`); expect(exercisesResponse.status()).toBe(200); const exercises = await exercisesResponse.json(); const recommendResponse = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "intermediate", goals: ["strength"], available_time: 45 } }); expect([200, 400]).toContain(recommendResponse.status()); }); // Test 17: Error handling - HTTP status codes test("API returns appropriate HTTP status codes", async ({ request }) => { const endpoints = [ { method: "get", url: `${API_URL}/exercises`, expectedStatus: 200 }, { method: "post", url: `${API_URL}/exercises`, expectedStatus: 400, data: { description: "missing name" } }, { method: "get", url: `${API_URL}/exercises/nonexistent`, expectedStatus: 404 } ]; for (const endpoint of endpoints) { let response; if (endpoint.method === "get") { response = await request.get(endpoint.url); } else { response = await request.post(endpoint.url, { data: endpoint.data }); } expect(response.status()).toBe(endpoint.expectedStatus); } }); // Test 18: Response content-type validation test("API responses have correct content-type", async ({ request }) => { const response = await request.get(`${API_URL}/exercises`); expect(response.status()).toBe(200); const contentType = response.headers()["content-type"]; expect(contentType).toContain("application/json"); }); // Test 19: POST with comma-separated goals test("POST /api/exercises/recommend with comma-separated goals", async ({ request }) => { const response = await request.post(`${API_URL}/exercises/recommend`, { data: { fitness_level: "advanced", goals: "strength,hypertrophy", available_time: 60 } }); expect([200, 400]).toContain(response.status()); }); // Test 20: Data validation - empty string handling test("POST /api/exercises rejects empty name string", async ({ request }) => { const response = await request.post(`${API_URL}/exercises`, { data: { name: " ", difficulty: "beginner", muscle_groups: [], equipment_needed: [] } }); expect(response.status()).toBe(400); const data = await response.json(); expect(data.error).toContain("Validation failed"); }); });