263 lines
9.3 KiB
JavaScript
263 lines
9.3 KiB
JavaScript
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");
|
|
});
|
|
});
|