diff --git a/packages/reservation-platform/cypress.config.ts b/packages/reservation-platform/cypress.config.ts new file mode 100644 index 0000000..0fe3c2c --- /dev/null +++ b/packages/reservation-platform/cypress.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'cypress' + +export default defineConfig({ + e2e: { + baseUrl: 'http://localhost:3000', + supportFile: false, + specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', + }, + component: { + devServer: { + framework: 'next', + bundler: 'webpack', + }, + }, + env: { + backendUrl: 'http://localhost:5000', + }, +}) \ No newline at end of file diff --git a/packages/reservation-platform/cypress/docker-compose.test.yml b/packages/reservation-platform/cypress/docker-compose.test.yml new file mode 100644 index 0000000..817bfbd --- /dev/null +++ b/packages/reservation-platform/cypress/docker-compose.test.yml @@ -0,0 +1,45 @@ +version: '3.8' + +services: + # Backend service + backend: + build: + context: ../../../backend + dockerfile: Dockerfile + container_name: myp-backend-test + ports: + - "5000:5000" + environment: + - SECRET_KEY=testsecretkey123456789 + - DATABASE_URL=sqlite:///myp.db + - FLASK_ENV=development + - TESTING=true + volumes: + - backend-test-data:/app/instance + restart: unless-stopped + networks: + - test-network + + # Optional: Frontend test service if needed + frontend-test: + image: cypress/included:13.6.1 + container_name: myp-frontend-test + depends_on: + - backend + environment: + - CYPRESS_baseUrl=http://host.docker.internal:3000 + - CYPRESS_backendUrl=http://backend:5000 + volumes: + - ..:/app + - ./cypress.config.ts:/app/cypress.config.ts + working_dir: /app + command: npx cypress run + networks: + - test-network + +networks: + test-network: + driver: bridge + +volumes: + backend-test-data: \ No newline at end of file diff --git a/packages/reservation-platform/cypress/e2e/home.cy.ts b/packages/reservation-platform/cypress/e2e/home.cy.ts new file mode 100644 index 0000000..676f77d --- /dev/null +++ b/packages/reservation-platform/cypress/e2e/home.cy.ts @@ -0,0 +1,19 @@ +describe('Homepage', () => { + beforeEach(() => { + cy.visit('/') + }) + + it('loads the homepage successfully', () => { + cy.contains('Manage Your Printers') + cy.get('a[href="/printer"]').should('exist') + }) + + it('shows printer cards', () => { + cy.get('[class*="w-auto h-36"]').should('exist') + }) + + it('has working navigation', () => { + cy.get('header').should('exist') + cy.get('a[href="/"]').should('exist') + }) +}) \ No newline at end of file diff --git a/packages/reservation-platform/cypress/e2e/printers.cy.ts b/packages/reservation-platform/cypress/e2e/printers.cy.ts new file mode 100644 index 0000000..2251628 --- /dev/null +++ b/packages/reservation-platform/cypress/e2e/printers.cy.ts @@ -0,0 +1,30 @@ +describe('Printer Pages', () => { + it('loads printer detail page', () => { + // Setup a basic printer route with a simple mock + cy.intercept('GET', '/api/printers/*', { + id: '1', + name: 'Test Drucker', + description: 'Ein Testdrucker', + status: 'available' + }).as('getPrinter') + + cy.visit('/printer/1') + cy.wait('@getPrinter') + cy.contains('Test Drucker') + }) + + it('shows all printers', () => { + // Mock printers list + cy.intercept('GET', '/api/printers', [ + { id: '1', name: 'Drucker 1', status: 'available' }, + { id: '2', name: 'Drucker 2', status: 'in_use' }, + { id: '3', name: 'Drucker 3', status: 'maintenance' } + ]).as('getPrinters') + + cy.visit('/') + cy.wait('@getPrinters') + cy.contains('Drucker 1') + cy.contains('Drucker 2') + cy.contains('Drucker 3') + }) +}) \ No newline at end of file diff --git a/packages/reservation-platform/cypress/e2e/workflow.cy.ts b/packages/reservation-platform/cypress/e2e/workflow.cy.ts new file mode 100644 index 0000000..6a7836a --- /dev/null +++ b/packages/reservation-platform/cypress/e2e/workflow.cy.ts @@ -0,0 +1,52 @@ +describe('Printer reservation workflow', () => { + beforeEach(() => { + // Login without OAuth + cy.login() + + // Mock API responses + cy.intercept('GET', '/api/printers', [ + { + id: '1', + name: 'Drucker 1', + description: 'Test Drucker', + status: 'available', + printJobs: [] + } + ]).as('getPrinters') + }) + + it('allows a user to view and reserve a printer', () => { + cy.visit('/') + cy.wait('@getPrinters') + + // Check printer is shown and available + cy.contains('Drucker 1') + cy.contains('Verfügbar') + + // Start reservation process + cy.contains('Reservieren').click() + + // Fill in the form + cy.get('input[name="hours"]').clear().type('1') + cy.get('input[name="minutes"]').clear().type('30') + cy.get('textarea[name="comments"]').type('Testauftrag') + + // Mock job creation + cy.intercept('POST', '/api/printers/*/reserve', { + id: 'job-123', + printerId: '1', + userId: 'test-user-id', + startTime: new Date().toISOString(), + endTime: new Date(Date.now() + 90 * 60000).toISOString(), + status: 'active' + }).as('createJob') + + // Submit the form + cy.contains('button', 'Reservieren').click() + cy.wait('@createJob') + + // Verify successful reservation + cy.url().should('include', '/job/') + cy.contains('Druckauftrag').should('exist') + }) +}) \ No newline at end of file diff --git a/packages/reservation-platform/cypress/start-test-environment.sh b/packages/reservation-platform/cypress/start-test-environment.sh new file mode 100755 index 0000000..7630ba0 --- /dev/null +++ b/packages/reservation-platform/cypress/start-test-environment.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +# Script to start a test environment with backend and optional test runner + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PARENT_DIR="$(dirname "$SCRIPT_DIR")" +BACKEND_DIR="$(dirname "$(dirname "$PARENT_DIR")")/backend" + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo -e "${GREEN}Starting MYP test environment...${NC}" +echo "Script directory: $SCRIPT_DIR" +echo "Frontend directory: $PARENT_DIR" +echo "Backend directory: $BACKEND_DIR" + +# Check if Docker is available +if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed or not in PATH${NC}" + exit 1 +fi + +# Check if docker-compose is available +if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then + echo -e "${RED}Error: Neither docker-compose nor docker compose is available${NC}" + exit 1 +fi + +# Check if running in container +IN_CONTAINER=false +if [ -f /.dockerenv ]; then + IN_CONTAINER=true +fi + +# Check if Docker is available and if we need sudo +NEED_SUDO=false +if [ "$IN_CONTAINER" = false ]; then + if ! docker ps &> /dev/null; then + if sudo docker ps &> /dev/null; then + echo -e "${YELLOW}Warning: Docker daemon requires sudo access. Will use sudo for all Docker commands.${NC}" + NEED_SUDO=true + fi + fi +fi + +# Function to run docker compose (handles both docker-compose and docker compose syntax) +run_docker_compose() { + if [ "$NEED_SUDO" = true ]; then + if command -v docker-compose &> /dev/null; then + sudo docker-compose "$@" + else + sudo docker compose "$@" + fi + else + if command -v docker-compose &> /dev/null; then + docker-compose "$@" + else + docker compose "$@" + fi + fi +} + +# Check if backend Docker image exists +echo -e "${YELLOW}Checking for backend Docker image...${NC}" +cd "$SCRIPT_DIR" + +# Start the backend container +echo -e "${GREEN}Starting backend container...${NC}" +run_docker_compose -f docker-compose.test.yml up -d backend + +# Wait for backend to be ready +echo -e "${YELLOW}Waiting for backend to be ready...${NC}" +max_attempts=30 +attempt=1 +backend_ready=false + +while [ $attempt -le $max_attempts ] && [ "$backend_ready" = "false" ]; do + echo "Checking backend readiness (attempt $attempt/$max_attempts)..." + + if curl -s http://localhost:5000/api/health 2>&1 | grep -q "healthy"; then + backend_ready=true + echo -e "${GREEN}Backend is ready!${NC}" + else + echo "Backend not ready yet, waiting..." + sleep 2 + attempt=$((attempt+1)) + fi +done + +if [ "$backend_ready" = "false" ]; then + echo -e "${RED}Backend failed to start properly after $max_attempts attempts${NC}" + echo "Logs from backend container:" + run_docker_compose -f docker-compose.test.yml logs backend + exit 1 +fi + +# Start frontend development server if it's not already running +if ! curl -s http://localhost:3000 > /dev/null; then + echo -e "${YELLOW}Starting frontend development server...${NC}" + cd "$PARENT_DIR" + + # Run in background + echo "Starting Next.js development server in the background..." + nohup pnpm dev > "$SCRIPT_DIR/frontend.log" 2>&1 & + + # Store the PID for later cleanup + FRONTEND_PID=$! + echo $FRONTEND_PID > "$SCRIPT_DIR/frontend.pid" + + echo -e "${GREEN}Frontend development server started with PID $FRONTEND_PID${NC}" + echo "Frontend logs available at: $SCRIPT_DIR/frontend.log" + + # Wait for frontend to be ready + echo -e "${YELLOW}Waiting for frontend to be ready...${NC}" + max_attempts=30 + attempt=1 + frontend_ready=false + + while [ $attempt -le $max_attempts ] && [ "$frontend_ready" = "false" ]; do + echo "Checking frontend readiness (attempt $attempt/$max_attempts)..." + + if curl -s http://localhost:3000 > /dev/null; then + frontend_ready=true + echo -e "${GREEN}Frontend is ready!${NC}" + else + echo "Frontend not ready yet, waiting..." + sleep 2 + attempt=$((attempt+1)) + fi + done + + if [ "$frontend_ready" = "false" ]; then + echo -e "${RED}Frontend failed to start properly${NC}" + exit 1 + fi +else + echo -e "${GREEN}Frontend already running at http://localhost:3000${NC}" +fi + +echo -e "${GREEN}Test environment is ready!${NC}" +echo "Backend is available at: http://localhost:5000" +echo "Frontend is available at: http://localhost:3000" +echo "" +echo "To run Cypress tests:" +echo " cd $PARENT_DIR && pnpm cypress" +echo "" +echo "To stop the test environment:" +echo " $SCRIPT_DIR/stop-test-environment.sh" \ No newline at end of file diff --git a/packages/reservation-platform/cypress/stop-test-environment.sh b/packages/reservation-platform/cypress/stop-test-environment.sh new file mode 100755 index 0000000..fd49cc1 --- /dev/null +++ b/packages/reservation-platform/cypress/stop-test-environment.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Script to stop the test environment + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PARENT_DIR="$(dirname "$SCRIPT_DIR")" + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo -e "${YELLOW}Stopping MYP test environment...${NC}" + +# Check if running in container +IN_CONTAINER=false +if [ -f /.dockerenv ]; then + IN_CONTAINER=true +fi + +# Check if Docker is available and if we need sudo +NEED_SUDO=false +if [ "$IN_CONTAINER" = false ]; then + if ! docker ps &> /dev/null; then + if sudo docker ps &> /dev/null; then + echo -e "${YELLOW}Warning: Docker daemon requires sudo access. Will use sudo for all Docker commands.${NC}" + NEED_SUDO=true + fi + fi +fi + +# Function to run docker compose (handles both docker-compose and docker compose syntax) +run_docker_compose() { + if [ "$NEED_SUDO" = true ]; then + if command -v docker-compose &> /dev/null; then + sudo docker-compose "$@" + else + sudo docker compose "$@" + fi + else + if command -v docker-compose &> /dev/null; then + docker-compose "$@" + else + docker compose "$@" + fi + fi +} + +# Stop the backend container +echo "Stopping backend containers..." +cd "$SCRIPT_DIR" +run_docker_compose -f docker-compose.test.yml down + +# Stop the frontend development server if we started it +if [ -f "$SCRIPT_DIR/frontend.pid" ]; then + FRONTEND_PID=$(cat "$SCRIPT_DIR/frontend.pid") + echo "Stopping frontend development server (PID: $FRONTEND_PID)..." + + if kill -0 $FRONTEND_PID 2>/dev/null; then + kill $FRONTEND_PID + echo "Frontend development server stopped" + else + echo "Frontend development server is not running with PID $FRONTEND_PID" + fi + + rm -f "$SCRIPT_DIR/frontend.pid" + rm -f "$SCRIPT_DIR/frontend.log" +else + echo "No frontend PID file found, assuming it was started externally" +fi + +echo -e "${GREEN}Test environment has been stopped${NC}" \ No newline at end of file diff --git a/packages/reservation-platform/cypress/support/commands.ts b/packages/reservation-platform/cypress/support/commands.ts new file mode 100644 index 0000000..a59feb4 --- /dev/null +++ b/packages/reservation-platform/cypress/support/commands.ts @@ -0,0 +1,33 @@ +// -- This is a parent command -- +Cypress.Commands.add('login', () => { + // Simulate logged in user without OAuth + window.localStorage.setItem('myp:user', JSON.stringify({ + id: 'test-user-id', + name: 'Test User', + email: 'test@example.com', + role: 'user' + })) +}) + +// -- This is a child command -- +Cypress.Commands.add('createPrintJob', (printerId, duration) => { + cy.intercept('POST', `/api/printers/${printerId}/reserve`, { + id: 'test-job-id', + printerId, + userId: 'test-user-id', + startTime: new Date().toISOString(), + endTime: new Date(Date.now() + duration * 60000).toISOString(), + status: 'active' + }).as('createJob') + + return cy.wrap('test-job-id') +}) + +declare global { + namespace Cypress { + interface Chainable { + login(): Chainable + createPrintJob(printerId: string, duration: number): Chainable + } + } +} \ No newline at end of file diff --git a/packages/reservation-platform/cypress/tsconfig.json b/packages/reservation-platform/cypress/tsconfig.json new file mode 100644 index 0000000..19dd89a --- /dev/null +++ b/packages/reservation-platform/cypress/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom"], + "types": ["cypress", "node"], + "baseUrl": "..", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts"] +} \ No newline at end of file