init projectmycelium
This commit is contained in:
		
							
								
								
									
										75
									
								
								tests/tests_archive/ux_suite/utils/assertions.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								tests/tests_archive/ux_suite/utils/assertions.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
//! UX Test Assertions
 | 
			
		||||
//! 
 | 
			
		||||
//! Custom assertion functions for UX testing
 | 
			
		||||
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
 | 
			
		||||
/// UX-specific assertions
 | 
			
		||||
pub struct UXAssertions;
 | 
			
		||||
 | 
			
		||||
impl UXAssertions {
 | 
			
		||||
    /// Assert ResponseBuilder envelope format
 | 
			
		||||
    pub fn assert_response_envelope(response: &Value) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if !response.get("success").is_some() {
 | 
			
		||||
            return Err("Missing 'success' field in response envelope".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let success = response["success"].as_bool().unwrap_or(false);
 | 
			
		||||
        
 | 
			
		||||
        if success {
 | 
			
		||||
            if !response.get("data").is_some() {
 | 
			
		||||
                return Err("Missing 'data' field in successful response".into());
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if !response.get("error").is_some() {
 | 
			
		||||
                return Err("Missing 'error' field in failed response".into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Assert currency format (TFC base currency)
 | 
			
		||||
    pub fn assert_currency_format(amount: &Value) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if !amount.get("amount").is_some() {
 | 
			
		||||
            return Err("Missing 'amount' field in currency object".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if !amount.get("currency").is_some() {
 | 
			
		||||
            return Err("Missing 'currency' field in currency object".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if !amount.get("formatted").is_some() {
 | 
			
		||||
            return Err("Missing 'formatted' field in currency object".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Validate currency is one of the supported types
 | 
			
		||||
        let currency = amount["currency"].as_str().unwrap_or("");
 | 
			
		||||
        match currency {
 | 
			
		||||
            "TFC" | "USD" | "EUR" | "CAD" => Ok(()),
 | 
			
		||||
            _ => Err(format!("Unsupported currency: {}", currency).into()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Assert cart consistency between API and UI
 | 
			
		||||
    pub fn assert_cart_consistency(api_count: usize, api_total: f64, ui_count: usize, ui_total: f64) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if api_count != ui_count {
 | 
			
		||||
            return Err(format!("Cart count mismatch: API shows {}, UI shows {}", api_count, ui_count).into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let total_diff = (api_total - ui_total).abs();
 | 
			
		||||
        if total_diff > 0.01 { // Allow for small floating point differences
 | 
			
		||||
            return Err(format!("Cart total mismatch: API shows {}, UI shows {}", api_total, ui_total).into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Assert page load performance
 | 
			
		||||
    pub fn assert_page_load_time(load_time_ms: u64, max_allowed_ms: u64) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if load_time_ms > max_allowed_ms {
 | 
			
		||||
            return Err(format!("Page load too slow: {}ms > {}ms", load_time_ms, max_allowed_ms).into());
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								tests/tests_archive/ux_suite/utils/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/tests_archive/ux_suite/utils/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
//! Test Utilities and Helpers
 | 
			
		||||
//! 
 | 
			
		||||
//! Common utilities and helper functions for UX testing
 | 
			
		||||
 | 
			
		||||
pub mod ux_test_helper;
 | 
			
		||||
pub mod assertions;
 | 
			
		||||
pub mod test_fixtures;
 | 
			
		||||
 | 
			
		||||
pub use ux_test_helper::*;
 | 
			
		||||
pub use assertions::*;
 | 
			
		||||
pub use test_fixtures::*;
 | 
			
		||||
							
								
								
									
										42
									
								
								tests/tests_archive/ux_suite/utils/test_fixtures.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/tests_archive/ux_suite/utils/test_fixtures.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
//! Test Fixtures
 | 
			
		||||
//! 
 | 
			
		||||
//! Common test data and setup helpers
 | 
			
		||||
 | 
			
		||||
use crate::environment::*;
 | 
			
		||||
 | 
			
		||||
/// Common test data
 | 
			
		||||
pub struct TestFixtures;
 | 
			
		||||
 | 
			
		||||
impl TestFixtures {
 | 
			
		||||
    /// Sample SSH public key for testing
 | 
			
		||||
    pub fn sample_ssh_public_key() -> &'static str {
 | 
			
		||||
        "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7vbqajDhA+17ZAdaZcZSjJF7Dp+iSbq3 test@example.com"
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Sample product data for testing
 | 
			
		||||
    pub fn sample_product_data() -> serde_json::Value {
 | 
			
		||||
        serde_json::json!({
 | 
			
		||||
            "id": "test-product-1",
 | 
			
		||||
            "name": "Test VM Instance",
 | 
			
		||||
            "category": "compute",
 | 
			
		||||
            "price": 15.0,
 | 
			
		||||
            "currency": "TFC",
 | 
			
		||||
            "description": "Test virtual machine instance"
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test environment with pre-configured data
 | 
			
		||||
    pub async fn setup_test_environment() -> Result<UXTestEnvironment, Box<dyn std::error::Error>> {
 | 
			
		||||
        // Initialize logging
 | 
			
		||||
        env_logger::builder()
 | 
			
		||||
            .filter_level(log::LevelFilter::Info)
 | 
			
		||||
            .try_init()
 | 
			
		||||
            .ok();
 | 
			
		||||
        
 | 
			
		||||
        // Create test environment
 | 
			
		||||
        let environment = UXTestEnvironment::new().await?;
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Test environment setup complete");
 | 
			
		||||
        Ok(environment)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										358
									
								
								tests/tests_archive/ux_suite/utils/ux_test_helper.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								tests/tests_archive/ux_suite/utils/ux_test_helper.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,358 @@
 | 
			
		||||
//! UX Test Helper
 | 
			
		||||
//! 
 | 
			
		||||
//! High-level interface for UX testing operations
 | 
			
		||||
 | 
			
		||||
use crate::environment::*;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use tokio::time::timeout;
 | 
			
		||||
 | 
			
		||||
/// Main UX test helper providing high-level testing operations
 | 
			
		||||
pub struct UXTestHelper<'a> {
 | 
			
		||||
    pub browser: &'a BrowserManager,
 | 
			
		||||
    pub api_client: &'a APITestClient,
 | 
			
		||||
    pub data_manager: &'a TestDataManager,
 | 
			
		||||
    current_user: Option<TestPersona>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> UXTestHelper<'a> {
 | 
			
		||||
    /// Create a new UX test helper
 | 
			
		||||
    pub fn new(environment: &'a UXTestEnvironment) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            browser: &environment.browser,
 | 
			
		||||
            api_client: &environment.api_client,
 | 
			
		||||
            data_manager: &environment.data_manager,
 | 
			
		||||
            current_user: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== AUTHENTICATION HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Register a new user
 | 
			
		||||
    pub async fn register_user(&mut self, persona: &TestPersona) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Registering user: {}", persona.email);
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to registration page
 | 
			
		||||
        self.browser.navigate_to("/register").await?;
 | 
			
		||||
        self.assert_page_loaded("Register").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Fill registration form
 | 
			
		||||
        self.browser.type_text("input[name='email']", &persona.email).await?;
 | 
			
		||||
        self.browser.type_text("input[name='password']", &persona.password).await?;
 | 
			
		||||
        self.browser.type_text("input[name='name']", &persona.name).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Submit form
 | 
			
		||||
        self.browser.click("button[type='submit']").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for success or error
 | 
			
		||||
        self.browser.wait_for_element(".alert, .notification, .success").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot(&format!("register_{}", self.safe_filename(&persona.email))).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Login as a test persona
 | 
			
		||||
    pub async fn login_as(&mut self, persona: &TestPersona) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Logging in as: {}", persona.email);
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to login page
 | 
			
		||||
        self.browser.navigate_to("/login").await?;
 | 
			
		||||
        self.assert_page_loaded("Login").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Fill login form
 | 
			
		||||
        self.browser.type_text("input[name='email']", &persona.email).await?;
 | 
			
		||||
        self.browser.type_text("input[name='password']", &persona.password).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Submit form
 | 
			
		||||
        self.browser.click("button[type='submit']").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for dashboard redirect or success
 | 
			
		||||
        self.browser.wait_for_element(".dashboard, .user-menu, .navbar-user").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify authentication
 | 
			
		||||
        self.assert_user_is_authenticated().await?;
 | 
			
		||||
        
 | 
			
		||||
        // Store current user
 | 
			
		||||
        self.current_user = Some(persona.clone());
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot(&format!("login_{}", self.safe_filename(&persona.email))).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Logout current user
 | 
			
		||||
    pub async fn logout(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Logging out current user");
 | 
			
		||||
        
 | 
			
		||||
        // Look for logout link/button
 | 
			
		||||
        if self.browser.element_exists(".user-menu").await {
 | 
			
		||||
            self.browser.click(".user-menu").await?;
 | 
			
		||||
            self.browser.wait_for_element("a[href='/logout']").await?;
 | 
			
		||||
            self.browser.click("a[href='/logout']").await?;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Direct navigation to logout
 | 
			
		||||
            self.browser.navigate_to("/logout").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Wait for redirect to home page
 | 
			
		||||
        self.browser.wait_for_element(".home, .login-prompt").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.current_user = None;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot("logout").await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== SHOPPING HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Add product to cart
 | 
			
		||||
    pub async fn add_product_to_cart(&mut self, product_id: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Adding product to cart: {}", product_id);
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to product or find it in marketplace
 | 
			
		||||
        self.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Find the product
 | 
			
		||||
        let product_selector = &format!("[data-product-id='{}']", product_id);
 | 
			
		||||
        self.browser.wait_for_element(product_selector).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Click add to cart button
 | 
			
		||||
        let add_to_cart_selector = &format!("{} .add-to-cart, {} button[data-action='add-to-cart']", product_selector, product_selector);
 | 
			
		||||
        self.browser.click(add_to_cart_selector).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for cart update notification
 | 
			
		||||
        self.browser.wait_for_element(".cart-updated, .notification, .alert-success").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot(&format!("add_to_cart_{}", product_id)).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Buy product directly (Buy Now)
 | 
			
		||||
    pub async fn buy_now(&mut self, product_id: &str) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Buying product directly: {}", product_id);
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to product
 | 
			
		||||
        self.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Find the product
 | 
			
		||||
        let product_selector = &format!("[data-product-id='{}']", product_id);
 | 
			
		||||
        self.browser.wait_for_element(product_selector).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Click buy now button
 | 
			
		||||
        let buy_now_selector = &format!("{} .buy-now, {} button[data-action='buy-now']", product_selector, product_selector);
 | 
			
		||||
        self.browser.click(buy_now_selector).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Complete checkout flow
 | 
			
		||||
        self.complete_checkout_flow().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Complete checkout flow
 | 
			
		||||
    pub async fn complete_checkout_flow(&mut self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        log::info!("Completing checkout flow");
 | 
			
		||||
        
 | 
			
		||||
        // Wait for checkout page or modal
 | 
			
		||||
        self.browser.wait_for_element(".checkout, .checkout-modal, .purchase-confirmation").await?;
 | 
			
		||||
        
 | 
			
		||||
        // If there's a confirm purchase button, click it
 | 
			
		||||
        if self.browser.element_exists("button[data-action='confirm-purchase']").await {
 | 
			
		||||
            self.browser.click("button[data-action='confirm-purchase']").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Wait for order confirmation
 | 
			
		||||
        self.browser.wait_for_element(".order-confirmation, .purchase-success").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Extract order ID if available
 | 
			
		||||
        let order_id = if let Ok(order_element) = self.browser.find_element(".order-id, [data-order-id]").await {
 | 
			
		||||
            order_element.text().await.unwrap_or_else(|_| "unknown".to_string())
 | 
			
		||||
        } else {
 | 
			
		||||
            "test-order-id".to_string()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot(&format!("order_confirmation_{}", order_id)).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(order_id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Navigate to cart
 | 
			
		||||
    pub async fn navigate_to_cart(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let cart_url = if self.current_user.is_some() {
 | 
			
		||||
            "/dashboard/cart"
 | 
			
		||||
        } else {
 | 
			
		||||
            "/cart"
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        self.browser.navigate_to(cart_url).await?;
 | 
			
		||||
        self.assert_page_loaded("Cart").await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== DASHBOARD HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Navigate to dashboard section
 | 
			
		||||
    pub async fn navigate_to_dashboard_section(&mut self, section: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("/dashboard/{}", section);
 | 
			
		||||
        self.browser.navigate_to(&url).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for dashboard content
 | 
			
		||||
        self.browser.wait_for_element(".dashboard-content, .dashboard-section").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot(&format!("dashboard_{}", section)).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Navigate to wallet
 | 
			
		||||
    pub async fn navigate_to_wallet(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.browser.navigate_to("/dashboard/wallet").await?;
 | 
			
		||||
        self.browser.wait_for_element(".wallet-dashboard, .wallet-content").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot("wallet_dashboard").await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Navigate to settings
 | 
			
		||||
    pub async fn navigate_to_settings(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.browser.navigate_to("/dashboard/settings").await?;
 | 
			
		||||
        self.browser.wait_for_element(".settings-form, .settings-content").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        self.browser.take_screenshot("settings_page").await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== SETTINGS HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Update profile information
 | 
			
		||||
    pub async fn update_profile(&mut self, name: &str, country: &str, timezone: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.navigate_to_settings().await?;
 | 
			
		||||
        
 | 
			
		||||
        // Fill profile form
 | 
			
		||||
        self.browser.type_text("input[name='name']", name).await?;
 | 
			
		||||
        self.browser.type_text("select[name='country']", country).await?;
 | 
			
		||||
        self.browser.type_text("select[name='timezone']", timezone).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Submit form
 | 
			
		||||
        self.browser.click("button[data-action='update-profile']").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for success notification
 | 
			
		||||
        self.browser.wait_for_element(".alert-success, .notification-success").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.browser.take_screenshot("profile_updated").await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Add SSH key
 | 
			
		||||
    pub async fn add_ssh_key(&mut self, key_name: &str, public_key: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.navigate_to_settings().await?;
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to SSH keys section
 | 
			
		||||
        if self.browser.element_exists("a[href='#ssh-keys']").await {
 | 
			
		||||
            self.browser.click("a[href='#ssh-keys']").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Fill SSH key form
 | 
			
		||||
        self.browser.type_text("input[name='ssh_key_name']", key_name).await?;
 | 
			
		||||
        self.browser.type_text("textarea[name='public_key']", public_key).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Submit form
 | 
			
		||||
        self.browser.click("button[data-action='add-ssh-key']").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for success notification
 | 
			
		||||
        self.browser.wait_for_element(".alert-success, .notification-success").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.browser.take_screenshot(&format!("ssh_key_added_{}", key_name)).await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== ASSERTION HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Assert that page has loaded with expected title
 | 
			
		||||
    pub async fn assert_page_loaded(&self, expected_title_part: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let title = self.browser.get_title().await?;
 | 
			
		||||
        if !title.to_lowercase().contains(&expected_title_part.to_lowercase()) {
 | 
			
		||||
            return Err(format!("Expected page title to contain '{}', got '{}'", expected_title_part, title).into());
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Assert user is authenticated
 | 
			
		||||
    pub async fn assert_user_is_authenticated(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Check for authentication indicators in UI
 | 
			
		||||
        if !self.browser.element_exists(".user-menu, .navbar-user, .dashboard-link").await {
 | 
			
		||||
            return Err("User does not appear to be authenticated (no user menu found)".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Validate with API
 | 
			
		||||
        let auth_status = self.api_client.validate_auth_status().await?;
 | 
			
		||||
        if !auth_status.authenticated {
 | 
			
		||||
            return Err("API reports user is not authenticated".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Assert cart item count
 | 
			
		||||
    pub async fn assert_cart_item_count(&self, expected_count: usize) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Check UI cart badge
 | 
			
		||||
        if let Ok(cart_badge) = self.browser.find_element(".cart-badge, .cart-count").await {
 | 
			
		||||
            let displayed_count = cart_badge.text().await?.parse::<usize>().unwrap_or(0);
 | 
			
		||||
            if displayed_count != expected_count {
 | 
			
		||||
                return Err(format!("Expected cart count {}, but UI shows {}", expected_count, displayed_count).into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Validate with API
 | 
			
		||||
        let cart_state = self.api_client.validate_cart_state().await?;
 | 
			
		||||
        if cart_state.item_count != expected_count {
 | 
			
		||||
            return Err(format!("Expected cart count {}, but API shows {}", expected_count, cart_state.item_count).into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Assert element contains text
 | 
			
		||||
    pub async fn assert_element_contains_text(&self, selector: &str, expected_text: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element_text = self.browser.get_text(selector).await?;
 | 
			
		||||
        if !element_text.contains(expected_text) {
 | 
			
		||||
            return Err(format!("Expected element '{}' to contain '{}', but got '{}'", selector, expected_text, element_text).into());
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ===== UTILITY HELPERS =====
 | 
			
		||||
 | 
			
		||||
    /// Create safe filename from email
 | 
			
		||||
    fn safe_filename(&self, email: &str) -> String {
 | 
			
		||||
        email.replace("@", "_at_").replace(".", "_dot_")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Take screenshot with timestamp
 | 
			
		||||
    pub async fn take_screenshot(&self, name: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.browser.take_screenshot(name).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for specific duration
 | 
			
		||||
    pub async fn wait(&self, duration: Duration) {
 | 
			
		||||
        tokio::time::sleep(duration).await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get current user
 | 
			
		||||
    pub fn current_user(&self) -> Option<&TestPersona> {
 | 
			
		||||
        self.current_user.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user