Integrate @bgotink/playwright-coverage for E2E test coverage tracking: Install @bgotink/playwright-coverage package Update playwright.config.js with coverage reporter Update test file imports to use coverage-enabled test function Add e2e-tests.yml coverage artifact upload and merge job Create codecov.yml with e2e flag configuration Add E2E coverage skill and VS Code task Coverage outputs: HTML, LCOV, JSON to coverage/e2e/ CI uploads merged coverage to Codecov with 'e2e' flag Enables unified coverage view across unit and E2E tests
295 lines
9.6 KiB
Bash
Executable File
295 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Test E2E Playwright Coverage - Execution Script
|
|
#
|
|
# Runs Playwright end-to-end tests with code coverage collection
|
|
# using @bgotink/playwright-coverage.
|
|
#
|
|
# IMPORTANT: For accurate source-level coverage, this script starts
|
|
# the Vite dev server (localhost:5173) which proxies API calls to
|
|
# the Docker backend (localhost:8080). V8 coverage requires source
|
|
# files to be accessible on the test host.
|
|
|
|
set -euo pipefail
|
|
|
|
# Source helper scripts
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SKILLS_SCRIPTS_DIR="$(cd "${SCRIPT_DIR}/../scripts" && pwd)"
|
|
|
|
# shellcheck source=../scripts/_logging_helpers.sh
|
|
source "${SKILLS_SCRIPTS_DIR}/_logging_helpers.sh"
|
|
# shellcheck source=../scripts/_error_handling_helpers.sh
|
|
source "${SKILLS_SCRIPTS_DIR}/_error_handling_helpers.sh"
|
|
# shellcheck source=../scripts/_environment_helpers.sh
|
|
source "${SKILLS_SCRIPTS_DIR}/_environment_helpers.sh"
|
|
|
|
# Project root is 3 levels up from this script
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
|
|
# Default parameter values
|
|
PROJECT="chromium"
|
|
VITE_PID=""
|
|
VITE_PORT="${VITE_PORT:-5173}" # Default Vite port (avoids conflicts with common ports)
|
|
BACKEND_URL="http://localhost:8080"
|
|
|
|
# Cleanup function to kill Vite dev server on exit
|
|
cleanup() {
|
|
if [[ -n "${VITE_PID}" ]] && kill -0 "${VITE_PID}" 2>/dev/null; then
|
|
log_info "Stopping Vite dev server (PID: ${VITE_PID})..."
|
|
kill "${VITE_PID}" 2>/dev/null || true
|
|
wait "${VITE_PID}" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Set up trap for cleanup
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# Parse command-line arguments
|
|
parse_arguments() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--project=*)
|
|
PROJECT="${1#*=}"
|
|
shift
|
|
;;
|
|
--project)
|
|
PROJECT="${2:-chromium}"
|
|
shift 2
|
|
;;
|
|
--skip-vite)
|
|
SKIP_VITE="true"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
log_warning "Unknown argument: $1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Show help message
|
|
show_help() {
|
|
cat << EOF
|
|
Usage: run.sh [OPTIONS]
|
|
|
|
Run Playwright E2E tests with coverage collection.
|
|
|
|
Coverage requires the Vite dev server to serve source files directly.
|
|
This script automatically starts Vite at localhost:5173, which proxies
|
|
API calls to the Docker backend at localhost:8080.
|
|
|
|
Options:
|
|
--project=PROJECT Browser project to run (chromium, firefox, webkit)
|
|
Default: chromium
|
|
--skip-vite Skip starting Vite dev server (use existing server)
|
|
-h, --help Show this help message
|
|
|
|
Environment Variables:
|
|
PLAYWRIGHT_BASE_URL Override test URL (default: http://localhost:5173)
|
|
VITE_PORT Vite dev server port (default: 5173)
|
|
CI Set to 'true' for CI environment
|
|
|
|
Prerequisites:
|
|
- Docker backend running at localhost:8080
|
|
- Node.js dependencies installed (npm ci)
|
|
|
|
Examples:
|
|
run.sh # Start Vite, run tests with coverage
|
|
run.sh --project=firefox # Run in Firefox with coverage
|
|
run.sh --skip-vite # Use existing Vite server
|
|
EOF
|
|
}
|
|
|
|
# Validate project parameter
|
|
validate_project() {
|
|
local valid_projects=("chromium" "firefox" "webkit")
|
|
local project_lower
|
|
project_lower=$(echo "${PROJECT}" | tr '[:upper:]' '[:lower:]')
|
|
|
|
for valid in "${valid_projects[@]}"; do
|
|
if [[ "${project_lower}" == "${valid}" ]]; then
|
|
PROJECT="${project_lower}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
error_exit "Invalid project '${PROJECT}'. Valid options: chromium, firefox, webkit"
|
|
}
|
|
|
|
# Check if backend is running
|
|
check_backend() {
|
|
log_info "Checking backend at ${BACKEND_URL}..."
|
|
local max_attempts=5
|
|
local attempt=1
|
|
|
|
while [[ ${attempt} -le ${max_attempts} ]]; do
|
|
if curl -sf "${BACKEND_URL}/api/v1/health" >/dev/null 2>&1; then
|
|
log_success "Backend is healthy"
|
|
return 0
|
|
fi
|
|
log_info "Waiting for backend... (attempt ${attempt}/${max_attempts})"
|
|
sleep 2
|
|
((attempt++))
|
|
done
|
|
|
|
log_warning "Backend not responding at ${BACKEND_URL}"
|
|
log_warning "Coverage tests require Docker backend. Start with:"
|
|
log_warning " docker compose -f .docker/compose/docker-compose.local.yml up -d"
|
|
return 1
|
|
}
|
|
|
|
# Start Vite dev server
|
|
start_vite() {
|
|
local vite_url="http://localhost:${VITE_PORT}"
|
|
|
|
# Check if Vite is already running on our preferred port
|
|
if curl -sf "${vite_url}" >/dev/null 2>&1; then
|
|
log_info "Vite dev server already running at ${vite_url}"
|
|
return 0
|
|
fi
|
|
|
|
log_step "VITE" "Starting Vite dev server"
|
|
cd "${PROJECT_ROOT}/frontend"
|
|
|
|
# Ensure dependencies are installed
|
|
if [[ ! -d "node_modules" ]]; then
|
|
log_info "Installing frontend dependencies..."
|
|
npm ci --silent
|
|
fi
|
|
|
|
# Start Vite in background with explicit port
|
|
log_command "npx vite --port ${VITE_PORT} (background)"
|
|
npx vite --port "${VITE_PORT}" > /tmp/vite.log 2>&1 &
|
|
VITE_PID=$!
|
|
|
|
# Wait for Vite to be ready (check log for actual port in case of conflict)
|
|
log_info "Waiting for Vite to start..."
|
|
local max_wait=60
|
|
local waited=0
|
|
local actual_port="${VITE_PORT}"
|
|
|
|
while [[ ${waited} -lt ${max_wait} ]]; do
|
|
# Check if Vite logged its ready message with actual port
|
|
if grep -q "Local:" /tmp/vite.log 2>/dev/null; then
|
|
# Extract actual port from Vite log (handles port conflict auto-switch)
|
|
actual_port=$(grep -oP 'localhost:\K[0-9]+' /tmp/vite.log 2>/dev/null | head -1 || echo "${VITE_PORT}")
|
|
vite_url="http://localhost:${actual_port}"
|
|
fi
|
|
|
|
if curl -sf "${vite_url}" >/dev/null 2>&1; then
|
|
# Update VITE_PORT if Vite chose a different port
|
|
if [[ "${actual_port}" != "${VITE_PORT}" ]]; then
|
|
log_warning "Port ${VITE_PORT} was busy, Vite using port ${actual_port}"
|
|
VITE_PORT="${actual_port}"
|
|
fi
|
|
log_success "Vite dev server ready at ${vite_url}"
|
|
cd "${PROJECT_ROOT}"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
((waited++))
|
|
done
|
|
|
|
log_error "Vite failed to start within ${max_wait} seconds"
|
|
log_error "Vite log:"
|
|
cat /tmp/vite.log 2>/dev/null || true
|
|
cd "${PROJECT_ROOT}"
|
|
return 1
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
SKIP_VITE="${SKIP_VITE:-false}"
|
|
parse_arguments "$@"
|
|
|
|
# Validate environment
|
|
log_step "ENVIRONMENT" "Validating prerequisites"
|
|
validate_node_environment "18.0" || error_exit "Node.js 18+ is required"
|
|
check_command_exists "npx" "npx is required (part of Node.js installation)"
|
|
|
|
# Validate project structure
|
|
log_step "VALIDATION" "Checking project structure"
|
|
cd "${PROJECT_ROOT}"
|
|
validate_project_structure "tests" "playwright.config.js" "package.json" || error_exit "Invalid project structure"
|
|
|
|
# Validate project parameter
|
|
validate_project
|
|
|
|
# Check backend is running (required for API proxy)
|
|
log_step "BACKEND" "Checking Docker backend"
|
|
if ! check_backend; then
|
|
error_exit "Backend not available. Coverage tests require Docker backend at ${BACKEND_URL}"
|
|
fi
|
|
|
|
# Start Vite dev server for coverage (unless skipped)
|
|
if [[ "${SKIP_VITE}" != "true" ]]; then
|
|
start_vite || error_exit "Failed to start Vite dev server"
|
|
fi
|
|
|
|
# Ensure coverage directory exists
|
|
log_step "SETUP" "Creating coverage directory"
|
|
mkdir -p coverage/e2e
|
|
|
|
# Set environment variables
|
|
# IMPORTANT: Use Vite URL (3000) for coverage, not Docker (8080)
|
|
export PLAYWRIGHT_HTML_OPEN="${PLAYWRIGHT_HTML_OPEN:-never}"
|
|
export PLAYWRIGHT_BASE_URL="${PLAYWRIGHT_BASE_URL:-http://localhost:${VITE_PORT}}"
|
|
|
|
# Log configuration
|
|
log_step "CONFIG" "Test configuration"
|
|
log_info "Project: ${PROJECT}"
|
|
log_info "Test URL: ${PLAYWRIGHT_BASE_URL}"
|
|
log_info "Backend URL: ${BACKEND_URL}"
|
|
log_info "Coverage output: ${PROJECT_ROOT}/coverage/e2e/"
|
|
log_info ""
|
|
log_info "Coverage architecture:"
|
|
log_info " Tests → Vite (localhost:3000) → serves source files"
|
|
log_info " Vite → Docker (localhost:8080) → API proxy"
|
|
|
|
# Execute Playwright tests with coverage
|
|
log_step "EXECUTION" "Running Playwright E2E tests with coverage"
|
|
log_command "npx playwright test --project=${PROJECT}"
|
|
|
|
local exit_code=0
|
|
if npx playwright test --project="${PROJECT}"; then
|
|
log_success "All E2E tests passed"
|
|
else
|
|
exit_code=$?
|
|
log_error "E2E tests failed (exit code: ${exit_code})"
|
|
fi
|
|
|
|
# Check if coverage was generated
|
|
log_step "COVERAGE" "Checking coverage output"
|
|
if [[ -f "coverage/e2e/lcov.info" ]]; then
|
|
log_success "E2E coverage generated: coverage/e2e/lcov.info"
|
|
|
|
# Print summary if coverage.json exists
|
|
if [[ -f "coverage/e2e/coverage.json" ]] && command -v jq &> /dev/null; then
|
|
log_info "📊 Coverage Summary:"
|
|
jq '.total' coverage/e2e/coverage.json 2>/dev/null || true
|
|
fi
|
|
|
|
# Show file sizes
|
|
log_info "Coverage files:"
|
|
ls -lh coverage/e2e/ 2>/dev/null || true
|
|
else
|
|
log_warning "No coverage data generated"
|
|
log_warning "Ensure test files import from '@bgotink/playwright-coverage'"
|
|
fi
|
|
|
|
# Output report locations
|
|
log_step "REPORTS" "Report locations"
|
|
log_info "Coverage HTML: ${PROJECT_ROOT}/coverage/e2e/index.html"
|
|
log_info "Coverage LCOV: ${PROJECT_ROOT}/coverage/e2e/lcov.info"
|
|
log_info "Playwright Report: ${PROJECT_ROOT}/playwright-report/index.html"
|
|
|
|
exit "${exit_code}"
|
|
}
|
|
|
|
# Run main with all arguments
|
|
main "$@"
|