#!/bin/bash # optimize-tests.sh - Script to optimize test execution and performance # Usage: ./bin/optimize-tests.sh [analyze|fix|profile] [--verbose] set -e # Colors for output GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' NC='\033[0m' # No Color # Default settings ACTION="" VERBOSE=false CURRENT_DATE=$(date +"%Y-%m-%d") # Parse arguments for arg in "$@"; do case $arg in analyze|fix|profile) ACTION="$arg" shift ;; --verbose) VERBOSE=true shift ;; esac done # Check if action is provided if [ -z "$ACTION" ]; then echo -e "${RED}Error: No action specified. Use: analyze, fix, or profile${NC}" exit 1 fi echo -e "${GREEN}=== Test Optimization - ${ACTION} ===${NC}" # Check if we're in the right directory if [ ! -d "tests/e2e" ]; then echo -e "${RED}Error: Please run this script from the wordpress-dev directory${NC}" exit 1 fi # Create logs directory mkdir -p logs/optimize LOG_FILE="logs/optimize/optimize-${ACTION}-${CURRENT_DATE}.log" # Log function log() { echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "$LOG_FILE" if [ "$VERBOSE" = true ] || [ -z "$2" ]; then echo "$1" else echo "$2" fi } # Function to analyze test performance analyze_test_performance() { log "Analyzing test performance..." # Check if we have test results if [ ! -d "test-results" ]; then log "No test results found. Please run tests first." return 1 fi # Create analysis directory mkdir -p "tests/e2e/analysis" ANALYSIS_FILE="tests/e2e/analysis/performance-${CURRENT_DATE}.json" log "Analyzing test duration..." # Extract test durations from result files echo "{" > "$ANALYSIS_FILE" echo " \"date\": \"${CURRENT_DATE}\"," >> "$ANALYSIS_FILE" echo " \"tests\": [" >> "$ANALYSIS_FILE" first=true find test-results -name "*.json" | while read -r file; do # Extract test name and duration test_name=$(grep -o '"title":"[^"]*"' "$file" | head -1 | cut -d'"' -f4) duration=$(grep -o '"duration":[0-9]*' "$file" | cut -d':' -f2) if [ -n "$test_name" ] && [ -n "$duration" ]; then # Convert duration to seconds duration_sec=$(echo "scale=2; $duration/1000" | bc) # Add to JSON if [ "$first" = true ]; then first=false else echo "," >> "$ANALYSIS_FILE" fi echo " {" >> "$ANALYSIS_FILE" echo " \"name\": \"${test_name}\"," >> "$ANALYSIS_FILE" echo " \"duration\": ${duration_sec}" >> "$ANALYSIS_FILE" echo -n " }" >> "$ANALYSIS_FILE" # Log slow tests if (( $(echo "$duration_sec > 5" | bc -l) )); then log "Slow test detected: ${test_name} (${duration_sec}s)" " - Slow: ${test_name} (${duration_sec}s)" fi fi done echo "" >> "$ANALYSIS_FILE" echo " ]" >> "$ANALYSIS_FILE" echo "}" >> "$ANALYSIS_FILE" log "Performance analysis saved to ${ANALYSIS_FILE}" "Performance analysis complete." # Analyze wait operations in test files log "Analyzing wait operations in test files..." WAIT_ANALYSIS="tests/e2e/analysis/waits-${CURRENT_DATE}.txt" echo "Wait Operations Analysis - ${CURRENT_DATE}" > "$WAIT_ANALYSIS" echo "=================================" >> "$WAIT_ANALYSIS" echo "" >> "$WAIT_ANALYSIS" # Find all timeouts in test files fixed_timeouts=$(grep -r "timeout:" --include="*.ts" tests/e2e | wc -l) fixed_waits=$(grep -r "waitForTimeout" --include="*.ts" tests/e2e | wc -l) echo "Fixed Timeouts: ${fixed_timeouts}" >> "$WAIT_ANALYSIS" echo "Fixed Waits: ${fixed_waits}" >> "$WAIT_ANALYSIS" echo "" >> "$WAIT_ANALYSIS" # List files with the most timeouts echo "Files with most timeouts:" >> "$WAIT_ANALYSIS" grep -r "timeout:" --include="*.ts" tests/e2e | cut -d: -f1 | sort | uniq -c | sort -nr | head -5 >> "$WAIT_ANALYSIS" echo "" >> "$WAIT_ANALYSIS" echo "Files with most waitForTimeout calls:" >> "$WAIT_ANALYSIS" grep -r "waitForTimeout" --include="*.ts" tests/e2e | cut -d: -f1 | sort | uniq -c | sort -nr | head -5 >> "$WAIT_ANALYSIS" log "Wait analysis saved to ${WAIT_ANALYSIS}" "Wait analysis complete." # Analyze selector complexity log "Analyzing selector complexity..." SELECTOR_ANALYSIS="tests/e2e/analysis/selectors-${CURRENT_DATE}.txt" echo "Selector Complexity Analysis - ${CURRENT_DATE}" > "$SELECTOR_ANALYSIS" echo "===================================" >> "$SELECTOR_ANALYSIS" echo "" >> "$SELECTOR_ANALYSIS" # Find complex selectors echo "Complex selectors:" >> "$SELECTOR_ANALYSIS" grep -r "selector.*=" --include="*.ts" tests/e2e/pages | grep -v "import" | sort -u >> "$SELECTOR_ANALYSIS" echo "" >> "$SELECTOR_ANALYSIS" # Count selector types echo "Selector types:" >> "$SELECTOR_ANALYSIS" echo " ID selectors: $(grep -r "\('#" --include="*.ts" tests/e2e | wc -l)" >> "$SELECTOR_ANALYSIS" echo " Class selectors: $(grep -r "('\\." --include="*.ts" tests/e2e | wc -l)" >> "$SELECTOR_ANALYSIS" echo " Attribute selectors: $(grep -r "('\[" --include="*.ts" tests/e2e | wc -l)" >> "$SELECTOR_ANALYSIS" log "Selector analysis saved to ${SELECTOR_ANALYSIS}" "Selector analysis complete." # Summary log "Analysis summary:" log " - Test performance analysis: ${ANALYSIS_FILE}" log " - Wait operations analysis: ${WAIT_ANALYSIS}" log " - Selector complexity analysis: ${SELECTOR_ANALYSIS}" return 0 } # Function to fix common performance issues fix_performance_issues() { log "Fixing common performance issues..." # Fix waitForTimeout calls log "Replacing fixed waitForTimeout calls with explicit waits..." FIXED_COUNT=0 # Find all files with waitForTimeout files_with_timeouts=$(grep -l "waitForTimeout" --include="*.ts" tests/e2e) for file in $files_with_timeouts; do # Create backup cp "$file" "${file}.bak" # Replace waitForTimeout with explicit waits sed -i.tmp 's/await page.waitForTimeout(\([0-9]*\))/await page.waitForLoadState("networkidle")/' "$file" # Count replacements replacements=$(diff "$file" "${file}.bak" | grep "waitForTimeout" | wc -l) FIXED_COUNT=$((FIXED_COUNT + replacements)) # Remove temporary files rm -f "${file}.tmp" done log "Replaced ${FIXED_COUNT} waitForTimeout calls with explicit waits" "Fixed ${FIXED_COUNT} waitForTimeout calls." # Optimize selectors in page objects log "Optimizing selectors in page objects..." # Check LoginPage.ts specifically if [ -f "tests/e2e/pages/LoginPage.ts" ]; then # Check if it's already using the optimized selectors if ! grep -q "input\[name=\"log\"\]" "tests/e2e/pages/LoginPage.ts"; then log "Updating LoginPage.ts with optimized selectors..." # Create backup cp "tests/e2e/pages/LoginPage.ts" "tests/e2e/pages/LoginPage.ts.bak" # Update selectors sed -i.tmp 's/private readonly usernameInput = '\''#user_login'\'';/private readonly usernameInput = '\''input[name="log"]'\'';/' "tests/e2e/pages/LoginPage.ts" sed -i.tmp 's/private readonly passwordInput = '\''#user_pass'\'';/private readonly passwordInput = '\''input[name="pwd"]'\'';/' "tests/e2e/pages/LoginPage.ts" sed -i.tmp 's/private readonly loginButton = '\''#wp-submit'\'';/private readonly loginButton = '\''input[type="submit"]'\'';/' "tests/e2e/pages/LoginPage.ts" # Remove temporary files rm -f "tests/e2e/pages/LoginPage.ts.tmp" log "LoginPage.ts updated with optimized selectors" "LoginPage.ts optimized." else log "LoginPage.ts already using optimized selectors" "LoginPage.ts already optimized." fi fi # Optimize playwright.config.ts if [ -f "playwright.config.ts" ]; then log "Optimizing Playwright configuration..." # Create backup cp "playwright.config.ts" "playwright.config.ts.bak" # Check if already optimized if ! grep -q "workers: 2" "playwright.config.ts"; then # Add worker limit sed -i.tmp 's/use: {/use: {\n workers: 2,/' "playwright.config.ts" log "Updated Playwright configuration with worker limit" "Added worker limit to config." else log "Playwright configuration already has worker limit" "Worker limit already set." fi # Check for retry configuration if ! grep -q "retries:" "playwright.config.ts"; then # Add retry configuration sed -i.tmp 's/projects: \[/retries: process.env.CI ? 2 : 0,\n projects: \[/' "playwright.config.ts" log "Added retry configuration to Playwright config" "Added retry configuration." else log "Playwright configuration already has retry settings" "Retry settings already configured." fi # Remove temporary files rm -f "playwright.config.ts.tmp" fi log "Performance optimizations complete." return 0 } # Function to profile test execution profile_test_execution() { log "Profiling test execution..." # Create a profile test file PROFILE_TEST="tests/e2e/profile-test.spec.ts" log "Creating profile test..." cat > "$PROFILE_TEST" << 'EOF' import { test } from '@playwright/test'; import { LoginPage } from './pages/LoginPage'; import { TEST_USERS } from './data/test-users'; test('Profile test performance', async ({ page }) => { console.time('Total'); // Login page const loginPage = new LoginPage(page); console.time('Navigate to login'); await loginPage.navigate(); console.timeEnd('Navigate to login'); console.time('Login'); await loginPage.login(TEST_USERS.trainer.username, TEST_USERS.trainer.password); console.timeEnd('Login'); console.time('Load dashboard'); await page.waitForLoadState('networkidle'); console.timeEnd('Load dashboard'); console.time('Screenshot'); await page.screenshot({ path: 'screenshots/profile-test.png' }); console.timeEnd('Screenshot'); console.timeEnd('Total'); }); EOF log "Running profile test..." # Create profile output directory mkdir -p "tests/e2e/profile" PROFILE_OUTPUT="tests/e2e/profile/profile-${CURRENT_DATE}.txt" # Run the profile test with trace npx playwright test "$PROFILE_TEST" --trace on > "$PROFILE_OUTPUT" 2>&1 # Extract timing information log "Extracting timing information..." echo "Performance Profile - ${CURRENT_DATE}" > "tests/e2e/profile/summary-${CURRENT_DATE}.txt" echo "=================================" >> "tests/e2e/profile/summary-${CURRENT_DATE}.txt" echo "" >> "tests/e2e/profile/summary-${CURRENT_DATE}.txt" # Extract console.time entries grep -E "Navigate to login|Login|Load dashboard|Screenshot|Total" "$PROFILE_OUTPUT" | grep -v "console.time" >> "tests/e2e/profile/summary-${CURRENT_DATE}.txt" # Clean up rm "$PROFILE_TEST" log "Profile results:" cat "tests/e2e/profile/summary-${CURRENT_DATE}.txt" log "Profiling complete. Results saved to tests/e2e/profile/summary-${CURRENT_DATE}.txt" return 0 } # Execute action case $ACTION in "analyze") analyze_test_performance ;; "fix") fix_performance_issues ;; "profile") profile_test_execution ;; *) log "Invalid action: ${ACTION}" exit 1 ;; esac log "Test optimization ${ACTION} completed successfully." exit 0