Füge Cypress Konfiguration und Testumgebung hinzu
- Fügt cypress.config.ts für E2E und Komponenten Tests hinzu - Fügt Cypress Testskripte und Docker-Compose Konfiguration hinzu - Ermöglicht automatische E2E-Tests mit separater Testumgebung 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4e0fa33dee
commit
27e1b2d82c
18
packages/reservation-platform/cypress.config.ts
Normal file
18
packages/reservation-platform/cypress.config.ts
Normal file
@ -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',
|
||||||
|
},
|
||||||
|
})
|
@ -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:
|
19
packages/reservation-platform/cypress/e2e/home.cy.ts
Normal file
19
packages/reservation-platform/cypress/e2e/home.cy.ts
Normal file
@ -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')
|
||||||
|
})
|
||||||
|
})
|
30
packages/reservation-platform/cypress/e2e/printers.cy.ts
Normal file
30
packages/reservation-platform/cypress/e2e/printers.cy.ts
Normal file
@ -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')
|
||||||
|
})
|
||||||
|
})
|
52
packages/reservation-platform/cypress/e2e/workflow.cy.ts
Normal file
52
packages/reservation-platform/cypress/e2e/workflow.cy.ts
Normal file
@ -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')
|
||||||
|
})
|
||||||
|
})
|
151
packages/reservation-platform/cypress/start-test-environment.sh
Executable file
151
packages/reservation-platform/cypress/start-test-environment.sh
Executable file
@ -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"
|
73
packages/reservation-platform/cypress/stop-test-environment.sh
Executable file
73
packages/reservation-platform/cypress/stop-test-environment.sh
Executable file
@ -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}"
|
33
packages/reservation-platform/cypress/support/commands.ts
Normal file
33
packages/reservation-platform/cypress/support/commands.ts
Normal file
@ -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<void>
|
||||||
|
createPrintJob(printerId: string, duration: number): Chainable<string>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
packages/reservation-platform/cypress/tsconfig.json
Normal file
12
packages/reservation-platform/cypress/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es5", "dom"],
|
||||||
|
"types": ["cypress", "node"],
|
||||||
|
"baseUrl": "..",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user