#!/bin/bash # health-check.sh - Comprehensive health check for the testing and deployment environment # Usage: ./bin/health-check.sh [--verbose] [--fix] [--ci] set -e # Colors for output GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' NC='\033[0m' # No Color # Default settings VERBOSE=false FIX_ISSUES=false CI_MODE=false CURRENT_DATE=$(date +"%Y-%m-%d") # Parse arguments for arg in "$@"; do case $arg in --verbose) VERBOSE=true shift ;; --fix) FIX_ISSUES=true shift ;; --ci) CI_MODE=true shift ;; esac done echo -e "${GREEN}=== HVAC Community Events Health Check ===${NC}" echo "Checking system health for testing and deployment..." # 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/health HEALTH_LOG="logs/health/health-check-${CURRENT_DATE}.log" # Log function log() { echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "$HEALTH_LOG" if [ "$VERBOSE" = true ]; then echo "$1" else echo "$2" fi } # Function to check a component and report status check_component() { local component=$1 local check_command=$2 local fix_command=$3 echo -e "\n${YELLOW}Checking: ${component}${NC}" log "Checking component: ${component}" "Checking: ${component}..." if eval "${check_command}"; then echo -e "${GREEN}✓ ${component} check passed${NC}" log "✓ ${component} check passed" return 0 else echo -e "${RED}✗ ${component} check failed${NC}" log "✗ ${component} check failed" if [ -n "$fix_command" ] && [ "$FIX_ISSUES" = true ]; then echo -e "${YELLOW}Attempting to fix: ${component}${NC}" log "Attempting to fix: ${component}" "Attempting to fix..." if eval "${fix_command}"; then echo -e "${GREEN}✓ ${component} fixed successfully${NC}" log "✓ ${component} fixed successfully" return 0 else echo -e "${RED}✗ Failed to fix: ${component}${NC}" log "✗ Failed to fix: ${component}" return 1 fi fi return 1 fi } # Function to check npm dependencies check_npm_dependencies() { log "Checking npm dependencies" "Checking npm dependencies..." # Check if package.json exists if [ ! -f "package.json" ]; then log "package.json not found" "package.json not found" return 1 fi # Check if node_modules exists if [ ! -d "node_modules" ]; then log "node_modules not found" "node_modules not found" return 1 } # Check Playwright installation if ! npm list @playwright/test > /dev/null 2>&1; then log "Playwright not installed properly" "Playwright not installed properly" return 1 fi log "npm dependencies check passed" "npm dependencies ok" return 0 } # Function to fix npm dependencies fix_npm_dependencies() { log "Fixing npm dependencies" "Fixing npm dependencies..." # Install dependencies npm install # Install Playwright browsers npx playwright install log "npm dependencies fixed" "npm dependencies fixed" return 0 } # Function to check test configuration check_test_config() { log "Checking test configuration" "Checking test configuration..." # Check if playwright.config.ts exists if [ ! -f "playwright.config.ts" ]; then log "playwright.config.ts not found" "playwright.config.ts not found" return 1 fi # Check if config files exist if [ ! -f "tests/e2e/config/staging-config.ts" ]; then log "staging-config.ts not found" "staging-config.ts not found" return 1 fi log "Test configuration check passed" "Test configuration ok" return 0 } # Function to check test data check_test_data() { log "Checking test data" "Checking test data..." # Check if test data directory exists if [ ! -d "tests/e2e/data" ]; then log "tests/e2e/data directory not found" "tests/e2e/data directory not found" return 1 fi # Check if essential test data files exist if [ ! -f "tests/e2e/data/test-users.ts" ] || [ ! -f "tests/e2e/data/test-events.ts" ]; then log "Essential test data files missing" "Essential test data files missing" return 1 fi log "Test data check passed" "Test data ok" return 0 } # Function to fix test data fix_test_data() { log "Fixing test data" "Fixing test data..." # Generate test data bash bin/test-data-manager.sh generate log "Test data fixed" "Test data fixed" return 0 } # Function to check page objects check_page_objects() { log "Checking page objects" "Checking page objects..." # Check if page object directory exists if [ ! -d "tests/e2e/pages" ]; then log "tests/e2e/pages directory not found" "tests/e2e/pages directory not found" return 1 fi # Check if essential page objects exist if [ ! -f "tests/e2e/pages/BasePage.ts" ] || [ ! -f "tests/e2e/pages/LoginPage.ts" ]; then log "Essential page objects missing" "Essential page objects missing" return 1 fi # Check selectors in LoginPage if ! grep -q "input\[name=\"log\"\]" "tests/e2e/pages/LoginPage.ts"; then log "LoginPage.ts missing updated selectors" "LoginPage.ts missing updated selectors" return 1 fi log "Page objects check passed" "Page objects ok" return 0 } # Function to fix page objects fix_page_objects() { log "Fixing page objects" "Fixing page objects..." # Create required directories mkdir -p tests/e2e/pages # Fix LoginPage.ts with updated selectors cat > "tests/e2e/pages/LoginPage.ts" << 'EOF' import { Page, expect } from '@playwright/test'; import { BasePage } from './BasePage'; import { PATHS } from '../config/staging-config'; /** * Page object representing the login page */ export class LoginPage extends BasePage { // Login form elements based on debug analysis private readonly usernameInput = 'input[name="log"]'; private readonly passwordInput = 'input[name="pwd"]'; private readonly loginButton = 'input[type="submit"]'; private readonly rememberMeCheckbox = 'input[name="rememberme"]'; private readonly loginError = '.login-error, .login_error, #login_error, .notice-error, .woocommerce-error, .wp-die-message'; private readonly forgotPasswordLink = 'a:text("Lost your password?")'; private readonly loginForm = 'form#hvac_community_loginform'; constructor(page: Page) { super(page); } /** * Navigate to the login page */ async navigate(): Promise { this.log('Navigating to login page'); await this.page.goto(PATHS.login); await this.page.waitForLoadState('networkidle'); // Make sure the form is visible before proceeding await this.page.waitForSelector(this.loginForm, { timeout: 10000 }); } /** * Alternative name for navigate for backward compatibility */ async navigateToLogin(): Promise { await this.navigate(); } /** * Login with provided credentials * @param username Username or email * @param password Password */ async login(username: string, password: string): Promise { this.log(`Logging in as ${username}`); // Wait for form elements to be ready await this.page.waitForSelector(this.usernameInput, { state: 'visible', timeout: 10000 }); await this.page.waitForSelector(this.passwordInput, { state: 'visible', timeout: 5000 }); await this.page.waitForSelector(this.loginButton, { state: 'visible', timeout: 5000 }); // Fill in the credentials await this.page.fill(this.usernameInput, username); await this.page.fill(this.passwordInput, password); // Click login and wait for navigation await this.page.click(this.loginButton); await this.page.waitForLoadState('networkidle'); this.log('Login form submitted'); } /** * Check if we're logged in */ async isLoggedIn(): Promise { await this.page.waitForLoadState('networkidle'); const url = await this.getUrl(); return url.includes('hvac-dashboard'); } /** * Check if username field is visible */ async isUsernameFieldVisible(): Promise { try { await this.page.waitForSelector(this.usernameInput, { state: 'visible', timeout: 5000 }); return true; } catch (error) { return false; } } /** * Get error message if login failed */ async getErrorMessage(): Promise { // Check all possible error selectors const errorSelectors = this.loginError.split(', '); for (const selector of errorSelectors) { if (await this.page.isVisible(selector)) { return await this.page.textContent(selector); } } // Check for any text containing common error messages const pageContent = await this.page.content(); if (pageContent.includes('Invalid username') || pageContent.includes('incorrect password') || pageContent.includes('Unknown username') || pageContent.includes('Error:')) { // Try to find error message in the page content const errorText = await this.page.evaluate(() => { const errorElements = Array.from(document.querySelectorAll('p, div, span')) .filter(el => el.textContent && (el.textContent.includes('Invalid') || el.textContent.includes('Error') || el.textContent.includes('incorrect') || el.textContent.includes('Unknown'))); return errorElements.length > 0 ? errorElements[0].textContent : null; }); return errorText; } return null; } /** * Click on "forgot password" link */ async clickForgotPassword(): Promise { await this.page.click(this.forgotPasswordLink); await this.page.waitForLoadState('networkidle'); } /** * Toggle "remember me" checkbox * @param check If true, check the box; if false, uncheck it */ async setRememberMe(check: boolean): Promise { const isChecked = await this.page.isChecked(this.rememberMeCheckbox); if (check !== isChecked) { await this.page.click(this.rememberMeCheckbox); } } } EOF # Create BasePage.ts if missing if [ ! -f "tests/e2e/pages/BasePage.ts" ]; then cat > "tests/e2e/pages/BasePage.ts" << 'EOF' import { Page } from '@playwright/test'; import { STAGING_URL } from '../config/staging-config'; /** * Base page object with common functionality for all pages */ export class BasePage { protected page: Page; constructor(page: Page) { this.page = page; } /** * Get the current URL */ async getUrl(): Promise { return this.page.url(); } /** * Wait for page to load completely */ async waitForPageLoad(): Promise { await this.page.waitForLoadState('networkidle'); } /** * Log a message with the page class name */ protected log(message: string): void { console.log(`[${this.constructor.name}] ${message}`); } /** * Take a screenshot with a descriptive name */ async takeScreenshot(name: string): Promise { const screenshotName = `${this.constructor.name}-${name}-${Date.now()}.png`; await this.page.screenshot({ path: `screenshots/${screenshotName}` }); this.log(`Screenshot saved: ${screenshotName}`); } /** * Check if an element is visible */ async isElementVisible(selector: string): Promise { return await this.page.isVisible(selector); } } EOF fi log "Page objects fixed" "Page objects fixed" return 0 } # Function to check scripts check_scripts() { log "Checking scripts" "Checking scripts..." # Check if bin directory exists if [ ! -d "bin" ]; then log "bin directory not found" "bin directory not found" return 1 fi # Check if essential scripts exist and are executable local missing_scripts=0 # Define essential scripts essential_scripts=( "verify-selectors.sh" "pre-deploy-validation.sh" "auto-recovery.sh" "test-data-manager.sh" ) for script in "${essential_scripts[@]}"; do if [ ! -f "bin/$script" ] || [ ! -x "bin/$script" ]; then log "Essential script missing or not executable: $script" "Missing script: $script" missing_scripts=$((missing_scripts + 1)) fi done if [ $missing_scripts -gt 0 ]; then log "$missing_scripts essential scripts missing" "$missing_scripts essential scripts missing" return 1 fi log "Scripts check passed" "Scripts ok" return 0 } # Function to check documentation check_documentation() { log "Checking documentation" "Checking documentation..." # Check if essential documentation exists local missing_docs=0 # Define essential documentation essential_docs=( "TESTING.md" "DEPLOYMENT-RESILIENCE.md" "TROUBLESHOOTING.md" "SELECTORS.md" ) for doc in "${essential_docs[@]}"; do if [ ! -f "$doc" ]; then log "Essential documentation missing: $doc" "Missing doc: $doc" missing_docs=$((missing_docs + 1)) fi done if [ $missing_docs -gt 0 ]; then log "$missing_docs essential documentation files missing" "$missing_docs essential docs missing" return 1 fi log "Documentation check passed" "Documentation ok" return 0 } # Function to check test results check_test_results() { log "Checking test results" "Checking test results..." # Check if test results directory exists if [ ! -d "test-results" ]; then log "No test results found. Tests may not have been run yet." "No test results found" return 0 # Not a failure, tests might not have been run yet fi # Count recent test results local recent_results=$(find test-results -type f -name "*.json" -mtime -1 | wc -l) if [ $recent_results -eq 0 ]; then log "No recent test results found in the last 24 hours" "No recent test results" return 0 # Not a failure, but worth noting fi # Check pass rate if we have results local total_tests=$(find test-results -name "*.json" | wc -l) local passed_tests=$(grep -r '"status":"passed"' test-results | wc -l) if [ $total_tests -gt 0 ]; then local pass_rate=$((passed_tests * 100 / total_tests)) log "Test pass rate: $pass_rate% ($passed_tests/$total_tests)" "Test pass rate: $pass_rate%" if [ $pass_rate -lt 80 ]; then log "Warning: Test pass rate below 80%" "Warning: Low pass rate" return 1 fi fi log "Test results check passed" "Test results ok" return 0 } # Function to check staging connectivity check_staging_connectivity() { log "Checking staging connectivity" "Checking staging connectivity..." # Ping the staging server (placeholder - update with actual staging URL) if ping -c 1 wordpress-974670-5399585.cloudwaysapps.com &> /dev/null; then log "Staging server is reachable" "Staging server reachable" else log "Warning: Cannot reach staging server" "Warning: Cannot reach staging" return 1 fi # Check if we can load the staging URL if curl -s --head https://wordpress-974670-5399585.cloudwaysapps.com/ | grep "200 OK" > /dev/null; then log "Staging URL returns 200 OK" "Staging URL OK" else log "Warning: Staging URL not returning 200 OK" "Warning: Staging URL not OK" return 1 fi log "Staging connectivity check passed" "Staging connectivity ok" return 0 } # Function to check for potential issues check_potential_issues() { log "Checking for potential issues" "Checking for potential issues..." # Check for uncommitted changes if [ -n "$(git status --porcelain)" ]; then log "Warning: Uncommitted changes detected" "Warning: Uncommitted changes" fi # Check for large files in test results local large_files=$(find test-results -type f -size +10M | wc -l) if [ $large_files -gt 0 ]; then log "Warning: $large_files large files (>10MB) found in test results" "Warning: Large test result files" fi # Check disk space local disk_usage=$(df -h . | awk 'NR==2 {print $5}' | tr -d '%') if [ $disk_usage -gt 90 ]; then log "Warning: Disk usage is high ($disk_usage%)" "Warning: High disk usage" return 1 fi log "Potential issues check passed" "No major issues found" return 0 } # Run all checks echo -e "\n${GREEN}=== Running Health Checks ===${NC}" check_component "npm dependencies" check_npm_dependencies fix_npm_dependencies NPM_STATUS=$? check_component "test configuration" check_test_config CONFIG_STATUS=$? check_component "test data" check_test_data fix_test_data DATA_STATUS=$? check_component "page objects" check_page_objects fix_page_objects PAGES_STATUS=$? check_component "scripts" check_scripts SCRIPTS_STATUS=$? check_component "documentation" check_documentation DOCS_STATUS=$? check_component "test results" check_test_results RESULTS_STATUS=$? check_component "staging connectivity" check_staging_connectivity STAGING_STATUS=$? check_component "potential issues" check_potential_issues ISSUES_STATUS=$? # Calculate overall health score TOTAL_CHECKS=9 PASSED_CHECKS=$(( (NPM_STATUS == 0 ? 1 : 0) + (CONFIG_STATUS == 0 ? 1 : 0) + (DATA_STATUS == 0 ? 1 : 0) + (PAGES_STATUS == 0 ? 1 : 0) + (SCRIPTS_STATUS == 0 ? 1 : 0) + (DOCS_STATUS == 0 ? 1 : 0) + (RESULTS_STATUS == 0 ? 1 : 0) + (STAGING_STATUS == 0 ? 1 : 0) + (ISSUES_STATUS == 0 ? 1 : 0) )) HEALTH_SCORE=$((PASSED_CHECKS * 100 / TOTAL_CHECKS)) # Summary echo -e "\n${GREEN}=== Health Check Summary ===${NC}" echo -e "Health Score: ${HEALTH_SCORE}% ($PASSED_CHECKS/$TOTAL_CHECKS)" if [ $HEALTH_SCORE -eq 100 ]; then echo -e "${GREEN}✓ All health checks passed${NC}" log "All health checks passed. Health Score: 100%" "All health checks passed" exit 0 elif [ $HEALTH_SCORE -ge 80 ]; then echo -e "${YELLOW}⚠ Some minor issues detected${NC}" log "Some minor issues detected. Health Score: $HEALTH_SCORE%" "Some minor issues detected" if [ "$CI_MODE" = true ]; then echo -e "CI mode enabled, but passing due to acceptable health score" exit 0 fi exit 0 else echo -e "${RED}✗ Significant issues detected${NC}" log "Significant issues detected. Health Score: $HEALTH_SCORE%" "Significant issues detected" if [ "$CI_MODE" = true ]; then echo -e "CI mode enabled, failing build due to low health score" exit 1 fi exit 1 fi