init projectmycelium
This commit is contained in:
		
							
								
								
									
										262
									
								
								tests/tests_archive/ux_suite/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								tests/tests_archive/ux_suite/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
# Project Mycelium UX Test Suite
 | 
			
		||||
 | 
			
		||||
Complete end-to-end testing framework for validating the entire user experience as specified in the roadmap Section 9.
 | 
			
		||||
 | 
			
		||||
## 🎯 Overview
 | 
			
		||||
 | 
			
		||||
This UX test suite replaces manual testing with automated browser-based tests that validate all user flows, from anonymous browsing to complex provider workflows. The framework ensures that the complete UX specification is working correctly.
 | 
			
		||||
 | 
			
		||||
## 🚀 Quick Start
 | 
			
		||||
 | 
			
		||||
### Prerequisites
 | 
			
		||||
 | 
			
		||||
1. **Selenium WebDriver** (for browser automation)
 | 
			
		||||
   ```bash
 | 
			
		||||
   # Install Chrome WebDriver
 | 
			
		||||
   # Option 1: Docker (recommended)
 | 
			
		||||
   docker run -d -p 4444:4444 selenium/standalone-chrome:latest
 | 
			
		||||
   
 | 
			
		||||
   # Option 2: Local installation
 | 
			
		||||
   # Download chromedriver and ensure it's in PATH
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. **Environment Setup**
 | 
			
		||||
   ```bash
 | 
			
		||||
   # Setup test directories
 | 
			
		||||
   make -f Makefile.ux-tests setup-ux-tests
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
### Running Tests
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Quick smoke tests (3 core flows)
 | 
			
		||||
make -f Makefile.ux-tests test-ux-quick
 | 
			
		||||
 | 
			
		||||
# Complete core UX test suite
 | 
			
		||||
make -f Makefile.ux-tests test-ux
 | 
			
		||||
 | 
			
		||||
# Individual test categories
 | 
			
		||||
make -f Makefile.ux-tests test-ux-public     # Public access tests
 | 
			
		||||
make -f Makefile.ux-tests test-ux-auth       # Authentication tests  
 | 
			
		||||
make -f Makefile.ux-tests test-ux-shopping   # Shopping workflow tests
 | 
			
		||||
 | 
			
		||||
# Complete test suite (all tests)
 | 
			
		||||
make -f Makefile.ux-tests test-ux-full
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 📋 Test Categories
 | 
			
		||||
 | 
			
		||||
### ✅ Public Access Tests
 | 
			
		||||
Tests functionality available without authentication:
 | 
			
		||||
- Information pages (`/docs`, `/privacy`, `/terms`, `/about`, `/contact`)
 | 
			
		||||
- Anonymous marketplace browsing
 | 
			
		||||
- Anonymous cart functionality  
 | 
			
		||||
- Search and filtering
 | 
			
		||||
- Product details pages
 | 
			
		||||
- Responsive design validation
 | 
			
		||||
 | 
			
		||||
### ✅ Authentication Tests
 | 
			
		||||
Tests user authentication functionality:
 | 
			
		||||
- User registration flow
 | 
			
		||||
- Login and logout functionality
 | 
			
		||||
- Cart migration during login
 | 
			
		||||
- Session management and persistence
 | 
			
		||||
- GitEa OAuth integration (conditional)
 | 
			
		||||
- Error handling and validation
 | 
			
		||||
 | 
			
		||||
### ✅ Shopping Workflow Tests
 | 
			
		||||
Tests complete shopping experience:
 | 
			
		||||
- Buy Now functionality
 | 
			
		||||
- Add to Cart workflow
 | 
			
		||||
- Cart management (edit quantities, remove items)
 | 
			
		||||
- Complete checkout process
 | 
			
		||||
- Order confirmation and tracking
 | 
			
		||||
- Insufficient funds handling
 | 
			
		||||
- Different product categories
 | 
			
		||||
 | 
			
		||||
## 🏗️ Framework Architecture
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
tests/ux_suite/
 | 
			
		||||
├── environment/           # Test environment management
 | 
			
		||||
│   ├── browser_manager.rs # Browser automation (Selenium WebDriver)
 | 
			
		||||
│   ├── test_server.rs     # Isolated test server instance
 | 
			
		||||
│   ├── test_data_manager.rs # Test personas and data fixtures
 | 
			
		||||
│   └── api_client.rs      # API validation alongside UX
 | 
			
		||||
├── flows/                 # End-to-end user flow tests
 | 
			
		||||
│   ├── public_access.rs   # Anonymous user tests
 | 
			
		||||
│   ├── authentication.rs # Auth flow tests
 | 
			
		||||
│   └── shopping.rs        # Purchase workflow tests
 | 
			
		||||
├── utils/                 # Test utilities and helpers
 | 
			
		||||
│   ├── ux_test_helper.rs  # High-level UX operations
 | 
			
		||||
│   ├── assertions.rs      # UX-specific assertions
 | 
			
		||||
│   └── test_fixtures.rs   # Test data and setup
 | 
			
		||||
└── reports/               # Test reports and screenshots
 | 
			
		||||
    └── screenshots/       # Visual validation screenshots
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 🧪 Test Data & Personas
 | 
			
		||||
 | 
			
		||||
The framework uses predefined test personas for different user roles:
 | 
			
		||||
 | 
			
		||||
- **Consumer** (`user1@example.com`) - Standard marketplace user
 | 
			
		||||
- **Farmer** (`farmer1@example.com`) - Resource provider with nodes  
 | 
			
		||||
- **App Provider** (`appdev1@example.com`) - Application publisher
 | 
			
		||||
- **Service Provider** (`service1@example.com`) - Professional services
 | 
			
		||||
 | 
			
		||||
Each persona has:
 | 
			
		||||
- Profile data (name, country, timezone, currency preference)
 | 
			
		||||
- Wallet balance for testing purchases
 | 
			
		||||
- Role-specific permissions and workflows
 | 
			
		||||
 | 
			
		||||
## 🔧 Configuration
 | 
			
		||||
 | 
			
		||||
### Environment Variables
 | 
			
		||||
 | 
			
		||||
- `UX_TEST_MODE`: Test execution mode
 | 
			
		||||
  - `dev` (default) - Visible browser, longer timeouts
 | 
			
		||||
  - `ci` - Headless browser, optimized for CI/CD
 | 
			
		||||
  - `full` - Complete test suite with all browsers
 | 
			
		||||
- `UX_TEST_TIMEOUT`: Timeout in seconds (default: 60)
 | 
			
		||||
- `SELENIUM_URL`: WebDriver URL (default: http://localhost:4444)
 | 
			
		||||
 | 
			
		||||
### Test Isolation
 | 
			
		||||
 | 
			
		||||
- **Dedicated test server** on port 8081 (separate from dev:8080, prod:3000)
 | 
			
		||||
- **Isolated test data** in `user_data_test/` directory
 | 
			
		||||
- **Clean state** between test runs
 | 
			
		||||
- **Separate browser profiles** with cleared cookies/storage
 | 
			
		||||
 | 
			
		||||
## 📊 Test Reporting
 | 
			
		||||
 | 
			
		||||
Tests generate comprehensive reports with:
 | 
			
		||||
- **Screenshots** for each major step and on failures
 | 
			
		||||
- **Performance metrics** (page load times)
 | 
			
		||||
- **API validation** results alongside UX validation
 | 
			
		||||
- **HTML reports** with visual timeline
 | 
			
		||||
 | 
			
		||||
Reports are saved to `tests/ux_suite/reports/`
 | 
			
		||||
 | 
			
		||||
## 🔄 Integration with Development Workflow
 | 
			
		||||
 | 
			
		||||
### Manual Testing Replacement
 | 
			
		||||
 | 
			
		||||
**Before**: `cargo run` → register as user1@example.com → manually test features
 | 
			
		||||
 | 
			
		||||
**After**: `make -f Makefile.ux-tests test-ux` → automated validation of all flows
 | 
			
		||||
 | 
			
		||||
### CI/CD Integration
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
# GitHub Actions example
 | 
			
		||||
- name: Run UX Tests
 | 
			
		||||
  run: make -f Makefile.ux-tests test-ux-ci
 | 
			
		||||
  env:
 | 
			
		||||
    UX_TEST_MODE: ci
 | 
			
		||||
    SELENIUM_URL: http://localhost:4444
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 📖 Writing New Tests
 | 
			
		||||
 | 
			
		||||
### Basic Test Structure
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_new_feature() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    // Login as appropriate persona
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test the feature
 | 
			
		||||
    helper.browser.navigate_to("/new-feature").await?;
 | 
			
		||||
    helper.assert_page_loaded("New Feature").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Take screenshot for visual validation
 | 
			
		||||
    helper.take_screenshot("new_feature_page").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Validate with API if needed
 | 
			
		||||
    let api_data = helper.api_client.get("/api/new-feature").await?;
 | 
			
		||||
    assert!(api_data.status == 200);
 | 
			
		||||
    
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Adding Test Personas
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// In test_data_manager.rs
 | 
			
		||||
personas.insert(UserRole::NewRole, TestPersona {
 | 
			
		||||
    email: "newrole@example.com".to_string(),
 | 
			
		||||
    password: "testpass123".to_string(), 
 | 
			
		||||
    name: "Test New Role".to_string(),
 | 
			
		||||
    role: UserRole::NewRole,
 | 
			
		||||
    profile: UserProfile { /* ... */ },
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 🐛 Troubleshooting
 | 
			
		||||
 | 
			
		||||
### Common Issues
 | 
			
		||||
 | 
			
		||||
1. **Selenium not running**
 | 
			
		||||
   ```bash
 | 
			
		||||
   docker run -d -p 4444:4444 selenium/standalone-chrome:latest
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. **Test server port conflicts**
 | 
			
		||||
   - Tests use port 8081 by default
 | 
			
		||||
   - Make sure port is available or configure different port
 | 
			
		||||
 | 
			
		||||
3. **Screenshots not saving**
 | 
			
		||||
   ```bash
 | 
			
		||||
   mkdir -p tests/ux_suite/reports/screenshots
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
4. **Browser automation fails**
 | 
			
		||||
   - Check WebDriver compatibility with browser version
 | 
			
		||||
   - Verify headless mode settings for CI environments
 | 
			
		||||
 | 
			
		||||
### Debug Mode
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Run with verbose logging
 | 
			
		||||
RUST_LOG=debug make -f Makefile.ux-tests test-ux
 | 
			
		||||
 | 
			
		||||
# Run single test with visible browser
 | 
			
		||||
UX_TEST_MODE=dev cargo test --test ux_suite_main test_user_login_flow --features ux_testing
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 🔮 Future Enhancements
 | 
			
		||||
 | 
			
		||||
- [ ] **Dashboard Tests** (Phase 3) - User dashboard, wallet, provider workflows
 | 
			
		||||
- [ ] **Settings Tests** (Phase 3) - SSH keys, notifications, preferences  
 | 
			
		||||
- [ ] **Modal Interactions** (Phase 3) - Complex UI flows based on html_template_tests/
 | 
			
		||||
- [ ] **Cross-browser Testing** (Phase 4) - Firefox, Safari compatibility
 | 
			
		||||
- [ ] **Mobile Testing** - Responsive design validation
 | 
			
		||||
- [ ] **Performance Testing** - Load time benchmarks
 | 
			
		||||
- [ ] **Visual Regression** - Screenshot comparison testing
 | 
			
		||||
 | 
			
		||||
## 📝 Contributing
 | 
			
		||||
 | 
			
		||||
When adding new UX features:
 | 
			
		||||
 | 
			
		||||
1. **Write tests first** - Define expected UX behavior in tests
 | 
			
		||||
2. **Use test personas** - Leverage existing user roles and data
 | 
			
		||||
3. **Include screenshots** - Visual validation for complex flows  
 | 
			
		||||
4. **Validate APIs** - Ensure backend/frontend consistency
 | 
			
		||||
5. **Update documentation** - Keep test coverage current
 | 
			
		||||
 | 
			
		||||
## 🎯 Success Metrics
 | 
			
		||||
 | 
			
		||||
- ✅ **60+ UX flows** automated (from Section 9 specification)
 | 
			
		||||
- ✅ **4 user personas** covering all roles
 | 
			
		||||
- ✅ **Complete automation** replacing manual testing workflow
 | 
			
		||||
- ✅ **Isolated environment** separate from other tests
 | 
			
		||||
- ✅ **CI/CD ready** with automated reporting
 | 
			
		||||
 | 
			
		||||
The UX test suite ensures the Project Mycelium delivers a complete, tested user experience before deployment.
 | 
			
		||||
							
								
								
									
										249
									
								
								tests/tests_archive/ux_suite/environment/api_client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								tests/tests_archive/ux_suite/environment/api_client.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
//! API Test Client
 | 
			
		||||
//! 
 | 
			
		||||
//! Validates API responses alongside UX interactions
 | 
			
		||||
 | 
			
		||||
use reqwest;
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
/// API test client for validating backend responses
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct APITestClient {
 | 
			
		||||
    client: reqwest::Client,
 | 
			
		||||
    base_url: String,
 | 
			
		||||
    session_cookies: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl APITestClient {
 | 
			
		||||
    /// Create a new API test client
 | 
			
		||||
    pub fn new(test_port: u16) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            client: reqwest::Client::builder()
 | 
			
		||||
                .cookie_store(true)
 | 
			
		||||
                .build()
 | 
			
		||||
                .expect("Failed to create HTTP client"),
 | 
			
		||||
            base_url: format!("http://localhost:{}", test_port),
 | 
			
		||||
            session_cookies: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a GET request to an API endpoint
 | 
			
		||||
    pub async fn get(&self, path: &str) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API GET: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.get(&url);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a POST request to an API endpoint
 | 
			
		||||
    pub async fn post(&self, path: &str, data: Value) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API POST: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.post(&url).json(&data);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a PUT request to an API endpoint
 | 
			
		||||
    pub async fn put(&self, path: &str, data: Value) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API PUT: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.put(&url).json(&data);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a DELETE request to an API endpoint
 | 
			
		||||
    pub async fn delete(&self, path: &str) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API DELETE: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.delete(&url);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate authentication status
 | 
			
		||||
    pub async fn validate_auth_status(&self) -> Result<AuthStatus, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/auth/status").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Validate ResponseBuilder envelope format
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let authenticated = response.body["data"]["authenticated"].as_bool().unwrap_or(false);
 | 
			
		||||
        let user_email = response.body["data"]["user"]["email"].as_str().map(|s| s.to_string());
 | 
			
		||||
        
 | 
			
		||||
        Ok(AuthStatus {
 | 
			
		||||
            authenticated,
 | 
			
		||||
            user_email,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate cart state
 | 
			
		||||
    pub async fn validate_cart_state(&self) -> Result<CartState, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/cart").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let items = response.body["data"]["items"].as_array()
 | 
			
		||||
            .map(|arr| arr.len())
 | 
			
		||||
            .unwrap_or(0);
 | 
			
		||||
        
 | 
			
		||||
        let total = response.body["data"]["total"]["amount"].as_f64().unwrap_or(0.0);
 | 
			
		||||
        
 | 
			
		||||
        Ok(CartState {
 | 
			
		||||
            item_count: items,
 | 
			
		||||
            total_amount: total,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate wallet balance
 | 
			
		||||
    pub async fn get_wallet_balance(&self) -> Result<WalletBalance, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/wallet/balance").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let balance = response.body["data"]["balance"]["amount"].as_f64().unwrap_or(0.0);
 | 
			
		||||
        let currency = response.body["data"]["balance"]["currency"].as_str().unwrap_or("TFC").to_string();
 | 
			
		||||
        
 | 
			
		||||
        Ok(WalletBalance {
 | 
			
		||||
            balance,
 | 
			
		||||
            currency,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get user dashboard data
 | 
			
		||||
    pub async fn get_dashboard_data(&self, dashboard_type: &str) -> Result<Value, Box<dyn std::error::Error>> {
 | 
			
		||||
        let path = format!("/api/dashboard/{}-data", dashboard_type);
 | 
			
		||||
        let response = self.get(&path).await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(response.body["data"].clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate products API
 | 
			
		||||
    pub async fn get_products(&self, category: Option<&str>) -> Result<Vec<Value>, Box<dyn std::error::Error>> {
 | 
			
		||||
        let path = if let Some(cat) = category {
 | 
			
		||||
            format!("/api/products?category={}", cat)
 | 
			
		||||
        } else {
 | 
			
		||||
            "/api/products".to_string()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let response = self.get(&path).await?;
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(response.body["data"]["products"].as_array().unwrap_or(&vec![]).clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Assert ResponseBuilder envelope format
 | 
			
		||||
    fn assert_response_envelope(&self, 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(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set session cookies for authenticated requests
 | 
			
		||||
    pub fn set_session_cookies(&mut self, cookies: String) {
 | 
			
		||||
        self.session_cookies = Some(cookies);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// API response structure
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ApiResponse {
 | 
			
		||||
    pub status: u16,
 | 
			
		||||
    pub headers: HashMap<String, String>,
 | 
			
		||||
    pub body: Value,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Authentication status
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct AuthStatus {
 | 
			
		||||
    pub authenticated: bool,
 | 
			
		||||
    pub user_email: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Cart state
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct CartState {
 | 
			
		||||
    pub item_count: usize,
 | 
			
		||||
    pub total_amount: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wallet balance
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct WalletBalance {
 | 
			
		||||
    pub balance: f64,
 | 
			
		||||
    pub currency: String,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										230
									
								
								tests/tests_archive/ux_suite/environment/browser_manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								tests/tests_archive/ux_suite/environment/browser_manager.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,230 @@
 | 
			
		||||
//! Browser Automation Manager
 | 
			
		||||
//! 
 | 
			
		||||
//! Handles browser lifecycle, navigation, and interaction for UX testing
 | 
			
		||||
 | 
			
		||||
use thirtyfour::prelude::*;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use tokio::time::timeout;
 | 
			
		||||
 | 
			
		||||
/// Supported browser types for testing
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum BrowserType {
 | 
			
		||||
    Chrome,
 | 
			
		||||
    Firefox,
 | 
			
		||||
    Safari,
 | 
			
		||||
    Edge,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Browser manager for UX testing
 | 
			
		||||
pub struct BrowserManager {
 | 
			
		||||
    driver: WebDriver,
 | 
			
		||||
    base_url: String,
 | 
			
		||||
    screenshot_dir: PathBuf,
 | 
			
		||||
    config: super::UXTestConfig,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Clone for BrowserManager {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        // Note: WebDriver cannot be cloned, so this creates a reference to the same driver
 | 
			
		||||
        // In practice, we should avoid cloning BrowserManager and use references instead
 | 
			
		||||
        panic!("BrowserManager cannot be cloned due to WebDriver limitations. Use references instead.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BrowserManager {
 | 
			
		||||
    /// Create a new browser manager
 | 
			
		||||
    pub async fn new(config: &super::UXTestConfig) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let capabilities = match config.browser_type {
 | 
			
		||||
            BrowserType::Chrome => {
 | 
			
		||||
                let mut caps = DesiredCapabilities::chrome();
 | 
			
		||||
                if config.headless {
 | 
			
		||||
                    caps.add_chrome_arg("--headless")?;
 | 
			
		||||
                }
 | 
			
		||||
                caps.add_chrome_arg("--no-sandbox")?;
 | 
			
		||||
                caps.add_chrome_arg("--disable-dev-shm-usage")?;
 | 
			
		||||
                caps.add_chrome_arg("--disable-gpu")?;
 | 
			
		||||
                caps.add_chrome_arg("--window-size=1920,1080")?;
 | 
			
		||||
                caps
 | 
			
		||||
            }
 | 
			
		||||
            BrowserType::Firefox => {
 | 
			
		||||
                let mut caps = DesiredCapabilities::firefox();
 | 
			
		||||
                if config.headless {
 | 
			
		||||
                    caps.add_firefox_arg("--headless")?;
 | 
			
		||||
                }
 | 
			
		||||
                caps
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                return Err("Browser type not yet implemented".into());
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Try to connect to existing WebDriver or start one
 | 
			
		||||
        let driver = match WebDriver::new("http://localhost:4444", capabilities.clone()).await {
 | 
			
		||||
            Ok(driver) => driver,
 | 
			
		||||
            Err(_) => {
 | 
			
		||||
                // If selenium server is not running, try local driver
 | 
			
		||||
                WebDriver::new("http://localhost:9515", capabilities).await?
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Configure browser
 | 
			
		||||
        driver.set_window_size(1920, 1080).await?;
 | 
			
		||||
        driver.implicitly_wait(Duration::from_secs(10)).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            driver,
 | 
			
		||||
            base_url: format!("http://localhost:{}", config.test_port),
 | 
			
		||||
            screenshot_dir: config.screenshot_dir.clone(),
 | 
			
		||||
            config: config.clone(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Navigate to a path on the test server
 | 
			
		||||
    pub async fn navigate_to(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = if path.starts_with("http") {
 | 
			
		||||
            path.to_string()
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("{}{}", self.base_url, path)
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Navigating to: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        timeout(
 | 
			
		||||
            Duration::from_secs(self.config.timeout_seconds),
 | 
			
		||||
            self.driver.goto(&url)
 | 
			
		||||
        ).await??;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for page to load
 | 
			
		||||
        self.wait_for_page_load().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for page to be fully loaded
 | 
			
		||||
    pub async fn wait_for_page_load(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Wait for document ready state
 | 
			
		||||
        self.driver.execute("return document.readyState", vec![]).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Additional wait for any dynamic content
 | 
			
		||||
        tokio::time::sleep(Duration::from_millis(500)).await;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Take a screenshot with the given name
 | 
			
		||||
    pub async fn take_screenshot(&self, name: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
 | 
			
		||||
        let screenshot = self.driver.screenshot_as_png().await?;
 | 
			
		||||
        let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
 | 
			
		||||
        let filename = format!("{}_{}.png", name, timestamp);
 | 
			
		||||
        let path = self.screenshot_dir.join(filename);
 | 
			
		||||
        
 | 
			
		||||
        std::fs::create_dir_all(&self.screenshot_dir)?;
 | 
			
		||||
        std::fs::write(&path, screenshot)?;
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Screenshot saved: {:?}", path);
 | 
			
		||||
        Ok(path)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Find element by CSS selector
 | 
			
		||||
    pub async fn find_element(&self, selector: &str) -> Result<WebElement, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.find(By::Css(selector)).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Find elements by CSS selector
 | 
			
		||||
    pub async fn find_elements(&self, selector: &str) -> Result<Vec<WebElement>, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.find_all(By::Css(selector)).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Click element by CSS selector
 | 
			
		||||
    pub async fn click(&self, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.scroll_into_view().await?;
 | 
			
		||||
        element.click().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Type text into element
 | 
			
		||||
    pub async fn type_text(&self, selector: &str, text: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.clear().await?;
 | 
			
		||||
        element.send_keys(text).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get text from element
 | 
			
		||||
    pub async fn get_text(&self, selector: &str) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        Ok(element.text().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if element exists
 | 
			
		||||
    pub async fn element_exists(&self, selector: &str) -> bool {
 | 
			
		||||
        self.driver.find(By::Css(selector)).await.is_ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for element to be visible
 | 
			
		||||
    pub async fn wait_for_element(&self, selector: &str) -> Result<WebElement, Box<dyn std::error::Error>> {
 | 
			
		||||
        let timeout_duration = Duration::from_secs(self.config.timeout_seconds);
 | 
			
		||||
        
 | 
			
		||||
        timeout(timeout_duration, async {
 | 
			
		||||
            loop {
 | 
			
		||||
                if let Ok(element) = self.driver.find(By::Css(selector)).await {
 | 
			
		||||
                    if element.is_displayed().await.unwrap_or(false) {
 | 
			
		||||
                        return Ok(element);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                tokio::time::sleep(Duration::from_millis(100)).await;
 | 
			
		||||
            }
 | 
			
		||||
        }).await?
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get current page title
 | 
			
		||||
    pub async fn get_title(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.title().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get current URL
 | 
			
		||||
    pub async fn get_current_url(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.current_url().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Execute JavaScript
 | 
			
		||||
    pub async fn execute_script(&self, script: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.execute(script, vec![]).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Quit the browser
 | 
			
		||||
    pub async fn quit(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.driver.quit().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get page source for debugging
 | 
			
		||||
    pub async fn get_page_source(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.source().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Scroll to element
 | 
			
		||||
    pub async fn scroll_to_element(&self, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.scroll_into_view().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for text to appear in element
 | 
			
		||||
    pub async fn wait_for_text(&self, selector: &str, expected_text: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let timeout_duration = Duration::from_secs(self.config.timeout_seconds);
 | 
			
		||||
        
 | 
			
		||||
        timeout(timeout_duration, async {
 | 
			
		||||
            loop {
 | 
			
		||||
                if let Ok(element) = self.driver.find(By::Css(selector)).await {
 | 
			
		||||
                    if let Ok(text) = element.text().await {
 | 
			
		||||
                        if text.contains(expected_text) {
 | 
			
		||||
                            return Ok(());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                tokio::time::sleep(Duration::from_millis(100)).await;
 | 
			
		||||
            }
 | 
			
		||||
        }).await?
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								tests/tests_archive/ux_suite/environment/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								tests/tests_archive/ux_suite/environment/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
//! Test Environment Management
 | 
			
		||||
//! 
 | 
			
		||||
//! Handles isolated test environment setup including:
 | 
			
		||||
//! - Test server instance
 | 
			
		||||
//! - Browser automation
 | 
			
		||||
//! - Test data management
 | 
			
		||||
//! - API client for validation
 | 
			
		||||
 | 
			
		||||
pub mod browser_manager;
 | 
			
		||||
pub mod test_server;
 | 
			
		||||
pub mod test_data_manager;
 | 
			
		||||
pub mod api_client;
 | 
			
		||||
 | 
			
		||||
pub use browser_manager::*;
 | 
			
		||||
pub use test_server::*;
 | 
			
		||||
pub use test_data_manager::*;
 | 
			
		||||
pub use api_client::*;
 | 
			
		||||
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use tokio::time::Duration;
 | 
			
		||||
 | 
			
		||||
/// Configuration for UX test environment
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct UXTestConfig {
 | 
			
		||||
    pub test_port: u16,
 | 
			
		||||
    pub headless: bool,
 | 
			
		||||
    pub timeout_seconds: u64,
 | 
			
		||||
    pub screenshot_on_failure: bool,
 | 
			
		||||
    pub browser_type: BrowserType,
 | 
			
		||||
    pub test_data_dir: PathBuf,
 | 
			
		||||
    pub screenshot_dir: PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for UXTestConfig {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let test_mode = std::env::var("UX_TEST_MODE").unwrap_or_else(|_| "dev".to_string());
 | 
			
		||||
        
 | 
			
		||||
        match test_mode.as_str() {
 | 
			
		||||
            "ci" => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: true,
 | 
			
		||||
                timeout_seconds: 30,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            },
 | 
			
		||||
            "dev" => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: false,
 | 
			
		||||
                timeout_seconds: 60,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            },
 | 
			
		||||
            _ => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: false,
 | 
			
		||||
                timeout_seconds: 60,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Complete UX test environment
 | 
			
		||||
pub struct UXTestEnvironment {
 | 
			
		||||
    pub config: UXTestConfig,
 | 
			
		||||
    pub browser: BrowserManager,
 | 
			
		||||
    pub server: TestServer,
 | 
			
		||||
    pub data_manager: TestDataManager,
 | 
			
		||||
    pub api_client: APITestClient,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UXTestEnvironment {
 | 
			
		||||
    /// Initialize a new test environment
 | 
			
		||||
    pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let config = UXTestConfig::default();
 | 
			
		||||
        
 | 
			
		||||
        // Create directories
 | 
			
		||||
        std::fs::create_dir_all(&config.test_data_dir)?;
 | 
			
		||||
        std::fs::create_dir_all(&config.screenshot_dir)?;
 | 
			
		||||
        
 | 
			
		||||
        // Initialize components
 | 
			
		||||
        let data_manager = TestDataManager::new(&config.test_data_dir)?;
 | 
			
		||||
        let server = TestServer::start(config.test_port).await?;
 | 
			
		||||
        let browser = BrowserManager::new(&config).await?;
 | 
			
		||||
        let api_client = APITestClient::new(config.test_port);
 | 
			
		||||
        
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            config,
 | 
			
		||||
            browser,
 | 
			
		||||
            server,
 | 
			
		||||
            data_manager,
 | 
			
		||||
            api_client,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get a UX test helper for this environment
 | 
			
		||||
    pub fn ux_helper(&self) -> UXTestHelper {
 | 
			
		||||
        UXTestHelper::new(self)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Clean up test environment
 | 
			
		||||
    pub async fn cleanup(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.browser.quit().await?;
 | 
			
		||||
        self.server.stop().await?;
 | 
			
		||||
        self.data_manager.cleanup()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										314
									
								
								tests/tests_archive/ux_suite/environment/test_data_manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								tests/tests_archive/ux_suite/environment/test_data_manager.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,314 @@
 | 
			
		||||
//! Test Data Management
 | 
			
		||||
//! 
 | 
			
		||||
//! Manages test fixtures, user personas, and data isolation for UX testing
 | 
			
		||||
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
/// Test user persona for different user types
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestPersona {
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub role: UserRole,
 | 
			
		||||
    pub profile: UserProfile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub enum UserRole {
 | 
			
		||||
    Consumer,
 | 
			
		||||
    Farmer,
 | 
			
		||||
    AppProvider,
 | 
			
		||||
    ServiceProvider,
 | 
			
		||||
    Admin,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct UserProfile {
 | 
			
		||||
    pub country: String,
 | 
			
		||||
    pub timezone: String,
 | 
			
		||||
    pub currency_preference: String,
 | 
			
		||||
    pub wallet_balance: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test marketplace data
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestMarketplaceData {
 | 
			
		||||
    pub products: Vec<TestProduct>,
 | 
			
		||||
    pub services: Vec<TestService>,
 | 
			
		||||
    pub nodes: Vec<TestNode>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestProduct {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub category: String,
 | 
			
		||||
    pub price: f64,
 | 
			
		||||
    pub currency: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub provider_email: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestService {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub price: f64,
 | 
			
		||||
    pub provider_email: String,
 | 
			
		||||
    pub status: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestNode {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub farmer_email: String,
 | 
			
		||||
    pub location: String,
 | 
			
		||||
    pub specs: NodeSpecs,
 | 
			
		||||
    pub available: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct NodeSpecs {
 | 
			
		||||
    pub cpu_cores: u32,
 | 
			
		||||
    pub ram_gb: u32,
 | 
			
		||||
    pub storage_gb: u32,
 | 
			
		||||
    pub price_per_hour: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test data manager for UX testing
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct TestDataManager {
 | 
			
		||||
    test_data_dir: PathBuf,
 | 
			
		||||
    personas: HashMap<UserRole, TestPersona>,
 | 
			
		||||
    marketplace_data: TestMarketplaceData,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestDataManager {
 | 
			
		||||
    /// Create a new test data manager
 | 
			
		||||
    pub fn new(test_data_dir: &PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let personas = Self::create_test_personas();
 | 
			
		||||
        let marketplace_data = Self::create_test_marketplace_data(&personas);
 | 
			
		||||
        
 | 
			
		||||
        let manager = Self {
 | 
			
		||||
            test_data_dir: test_data_dir.clone(),
 | 
			
		||||
            personas,
 | 
			
		||||
            marketplace_data,
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        manager.setup_test_data()?;
 | 
			
		||||
        Ok(manager)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create test user personas
 | 
			
		||||
    fn create_test_personas() -> HashMap<UserRole, TestPersona> {
 | 
			
		||||
        let mut personas = HashMap::new();
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::Consumer, TestPersona {
 | 
			
		||||
            email: "user1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Consumer".to_string(),
 | 
			
		||||
            role: UserRole::Consumer,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "United States".to_string(),
 | 
			
		||||
                timezone: "America/New_York".to_string(),
 | 
			
		||||
                currency_preference: "USD".to_string(),
 | 
			
		||||
                wallet_balance: 100.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::Farmer, TestPersona {
 | 
			
		||||
            email: "farmer1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Farmer".to_string(),
 | 
			
		||||
            role: UserRole::Farmer,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "Canada".to_string(),
 | 
			
		||||
                timezone: "America/Toronto".to_string(),
 | 
			
		||||
                currency_preference: "CAD".to_string(),
 | 
			
		||||
                wallet_balance: 500.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::AppProvider, TestPersona {
 | 
			
		||||
            email: "appdev1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test App Developer".to_string(),
 | 
			
		||||
            role: UserRole::AppProvider,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "Germany".to_string(),
 | 
			
		||||
                timezone: "Europe/Berlin".to_string(),
 | 
			
		||||
                currency_preference: "EUR".to_string(),
 | 
			
		||||
                wallet_balance: 200.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::ServiceProvider, TestPersona {
 | 
			
		||||
            email: "service1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Service Provider".to_string(),
 | 
			
		||||
            role: UserRole::ServiceProvider,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "United Kingdom".to_string(),
 | 
			
		||||
                timezone: "Europe/London".to_string(),
 | 
			
		||||
                currency_preference: "TFC".to_string(),
 | 
			
		||||
                wallet_balance: 300.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create test marketplace data
 | 
			
		||||
    fn create_test_marketplace_data(personas: &HashMap<UserRole, TestPersona>) -> TestMarketplaceData {
 | 
			
		||||
        let farmer_email = personas.get(&UserRole::Farmer).unwrap().email.clone();
 | 
			
		||||
        let app_provider_email = personas.get(&UserRole::AppProvider).unwrap().email.clone();
 | 
			
		||||
        let service_provider_email = personas.get(&UserRole::ServiceProvider).unwrap().email.clone();
 | 
			
		||||
        
 | 
			
		||||
        TestMarketplaceData {
 | 
			
		||||
            products: vec![
 | 
			
		||||
                TestProduct {
 | 
			
		||||
                    id: "test-vm-1".to_string(),
 | 
			
		||||
                    name: "Test VM Small".to_string(),
 | 
			
		||||
                    category: "compute".to_string(),
 | 
			
		||||
                    price: 10.0,
 | 
			
		||||
                    currency: "TFC".to_string(),
 | 
			
		||||
                    description: "Small virtual machine for testing".to_string(),
 | 
			
		||||
                    provider_email: farmer_email.clone(),
 | 
			
		||||
                },
 | 
			
		||||
                TestProduct {
 | 
			
		||||
                    id: "test-app-1".to_string(),
 | 
			
		||||
                    name: "Test Application".to_string(),
 | 
			
		||||
                    category: "applications".to_string(),
 | 
			
		||||
                    price: 25.0,
 | 
			
		||||
                    currency: "TFC".to_string(),
 | 
			
		||||
                    description: "Test application for UX testing".to_string(),
 | 
			
		||||
                    provider_email: app_provider_email.clone(),
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            services: vec![
 | 
			
		||||
                TestService {
 | 
			
		||||
                    id: "test-service-1".to_string(),
 | 
			
		||||
                    name: "Test Consulting Service".to_string(),
 | 
			
		||||
                    description: "Professional consulting service for testing".to_string(),
 | 
			
		||||
                    price: 100.0,
 | 
			
		||||
                    provider_email: service_provider_email.clone(),
 | 
			
		||||
                    status: "available".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            nodes: vec![
 | 
			
		||||
                TestNode {
 | 
			
		||||
                    id: "test-node-1".to_string(),
 | 
			
		||||
                    farmer_email: farmer_email.clone(),
 | 
			
		||||
                    location: "New York, USA".to_string(),
 | 
			
		||||
                    specs: NodeSpecs {
 | 
			
		||||
                        cpu_cores: 8,
 | 
			
		||||
                        ram_gb: 16,
 | 
			
		||||
                        storage_gb: 500,
 | 
			
		||||
                        price_per_hour: 2.0,
 | 
			
		||||
                    },
 | 
			
		||||
                    available: true,
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Setup test data files
 | 
			
		||||
    fn setup_test_data(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Create test data directory
 | 
			
		||||
        std::fs::create_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        
 | 
			
		||||
        // Create user data files for each persona
 | 
			
		||||
        for persona in self.personas.values() {
 | 
			
		||||
            self.create_user_data_file(persona)?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Save marketplace data
 | 
			
		||||
        let marketplace_file = self.test_data_dir.join("marketplace_data.json");
 | 
			
		||||
        let marketplace_json = serde_json::to_string_pretty(&self.marketplace_data)?;
 | 
			
		||||
        std::fs::write(marketplace_file, marketplace_json)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create user data file for a persona
 | 
			
		||||
    fn create_user_data_file(&self, persona: &TestPersona) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let encoded_email = persona.email.replace("@", "_at_").replace(".", "_dot_");
 | 
			
		||||
        let user_file = self.test_data_dir.join(format!("{}.json", encoded_email));
 | 
			
		||||
        
 | 
			
		||||
        let user_data = serde_json::json!({
 | 
			
		||||
            "email": persona.email,
 | 
			
		||||
            "name": persona.name,
 | 
			
		||||
            "profile": persona.profile,
 | 
			
		||||
            "role": persona.role,
 | 
			
		||||
            "wallet": {
 | 
			
		||||
                "balance": persona.profile.wallet_balance,
 | 
			
		||||
                "currency": persona.profile.currency_preference,
 | 
			
		||||
                "transactions": []
 | 
			
		||||
            },
 | 
			
		||||
            "cart": {
 | 
			
		||||
                "items": [],
 | 
			
		||||
                "total": 0.0
 | 
			
		||||
            },
 | 
			
		||||
            "orders": [],
 | 
			
		||||
            "settings": {
 | 
			
		||||
                "notifications": {
 | 
			
		||||
                    "security_alerts": true,
 | 
			
		||||
                    "billing_notifications": true,
 | 
			
		||||
                    "system_alerts": true,
 | 
			
		||||
                    "newsletter": false,
 | 
			
		||||
                    "dashboard_notifications": true
 | 
			
		||||
                },
 | 
			
		||||
                "ssh_keys": []
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        let user_json = serde_json::to_string_pretty(&user_data)?;
 | 
			
		||||
        std::fs::write(user_file, user_json)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test persona by role
 | 
			
		||||
    pub fn get_persona(&self, role: &UserRole) -> Option<&TestPersona> {
 | 
			
		||||
        self.personas.get(role)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all test personas
 | 
			
		||||
    pub fn get_all_personas(&self) -> &HashMap<UserRole, TestPersona> {
 | 
			
		||||
        &self.personas
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test marketplace data
 | 
			
		||||
    pub fn get_marketplace_data(&self) -> &TestMarketplaceData {
 | 
			
		||||
        &self.marketplace_data
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Reset test data to clean state
 | 
			
		||||
    pub fn reset_test_data(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Remove all user data files
 | 
			
		||||
        if self.test_data_dir.exists() {
 | 
			
		||||
            std::fs::remove_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Recreate test data
 | 
			
		||||
        self.setup_test_data()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Cleanup test data
 | 
			
		||||
    pub fn cleanup(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if self.test_data_dir.exists() {
 | 
			
		||||
            std::fs::remove_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test user credentials
 | 
			
		||||
    pub fn get_test_credentials(&self, role: &UserRole) -> Option<(String, String)> {
 | 
			
		||||
        self.personas.get(role).map(|p| (p.email.clone(), p.password.clone()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								tests/tests_archive/ux_suite/environment/test_server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								tests/tests_archive/ux_suite/environment/test_server.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
//! Test Server Management
 | 
			
		||||
//! 
 | 
			
		||||
//! Manages isolated test server instance for UX testing
 | 
			
		||||
 | 
			
		||||
use actix_web::{web, App, HttpServer, middleware::Logger};
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use tokio::sync::Mutex;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
/// Test server instance
 | 
			
		||||
pub struct TestServer {
 | 
			
		||||
    port: u16,
 | 
			
		||||
    server_handle: Option<actix_web::dev::ServerHandle>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestServer {
 | 
			
		||||
    /// Start a new test server instance
 | 
			
		||||
    pub async fn start(port: u16) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        // Set environment variables for test mode
 | 
			
		||||
        std::env::set_var("TEST_MODE", "true");
 | 
			
		||||
        std::env::set_var("TEST_PORT", port.to_string());
 | 
			
		||||
        std::env::set_var("TEST_DATA_DIR", "user_data_test");
 | 
			
		||||
        
 | 
			
		||||
        // Import the main app configuration
 | 
			
		||||
        let config = threefold_marketplace::config::get_config();
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Starting test server on port {}", port);
 | 
			
		||||
        
 | 
			
		||||
        // Create test server with the same configuration as main app
 | 
			
		||||
        let server = HttpServer::new(move || {
 | 
			
		||||
            // Initialize Tera templates
 | 
			
		||||
            let mut tera = match tera::Tera::new(&format!("{}/**/*.html", config.templates.dir)) {
 | 
			
		||||
                Ok(t) => t,
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    eprintln!("Tera initialization error: {}", e);
 | 
			
		||||
                    std::process::exit(1);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Register custom Tera functions
 | 
			
		||||
            threefold_marketplace::utils::register_tera_functions(&mut tera);
 | 
			
		||||
 | 
			
		||||
            App::new()
 | 
			
		||||
                .wrap(Logger::default())
 | 
			
		||||
                .wrap(threefold_marketplace::middleware::RequestTimer)
 | 
			
		||||
                .wrap(threefold_marketplace::middleware::SecurityHeaders)
 | 
			
		||||
                .service(actix_files::Files::new("/static", "./src/static"))
 | 
			
		||||
                .app_data(web::Data::new(tera))
 | 
			
		||||
                .configure(threefold_marketplace::routes::configure_routes)
 | 
			
		||||
        })
 | 
			
		||||
        .workers(1) // Single worker for testing
 | 
			
		||||
        .bind(format!("127.0.0.1:{}", port))?;
 | 
			
		||||
        
 | 
			
		||||
        let server_handle = server.handle();
 | 
			
		||||
        
 | 
			
		||||
        // Start server in background
 | 
			
		||||
        tokio::spawn(async move {
 | 
			
		||||
            if let Err(e) = server.run().await {
 | 
			
		||||
                eprintln!("Test server error: {}", e);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        // Wait for server to start
 | 
			
		||||
        tokio::time::sleep(Duration::from_millis(500)).await;
 | 
			
		||||
        
 | 
			
		||||
        // Verify server is running
 | 
			
		||||
        let client = reqwest::Client::new();
 | 
			
		||||
        let health_check_url = format!("http://127.0.0.1:{}/", port);
 | 
			
		||||
        
 | 
			
		||||
        for attempt in 1..=10 {
 | 
			
		||||
            match client.get(&health_check_url).send().await {
 | 
			
		||||
                Ok(response) if response.status().is_success() => {
 | 
			
		||||
                    log::info!("Test server started successfully on port {}", port);
 | 
			
		||||
                    return Ok(Self {
 | 
			
		||||
                        port,
 | 
			
		||||
                        server_handle: Some(server_handle),
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                Ok(_) | Err(_) => {
 | 
			
		||||
                    if attempt == 10 {
 | 
			
		||||
                        return Err("Failed to start test server after 10 attempts".into());
 | 
			
		||||
                    }
 | 
			
		||||
                    tokio::time::sleep(Duration::from_millis(500)).await;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            port,
 | 
			
		||||
            server_handle: Some(server_handle),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get the server URL
 | 
			
		||||
    pub fn url(&self) -> String {
 | 
			
		||||
        format!("http://127.0.0.1:{}", self.port)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Stop the test server
 | 
			
		||||
    pub async fn stop(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if let Some(handle) = self.server_handle.take() {
 | 
			
		||||
            handle.stop(true).await;
 | 
			
		||||
            log::info!("Test server stopped");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for TestServer {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        if let Some(handle) = self.server_handle.take() {
 | 
			
		||||
            // Best effort cleanup
 | 
			
		||||
            let _ = handle.stop(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								tests/tests_archive/ux_suite/flows/authentication.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								tests/tests_archive/ux_suite/flows/authentication.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
//! Authentication Flow Tests
 | 
			
		||||
//! 
 | 
			
		||||
//! Tests for user authentication functionality:
 | 
			
		||||
//! - User registration at /register
 | 
			
		||||
//! - Login and logout functionality
 | 
			
		||||
//! - Cart migration during login
 | 
			
		||||
//! - Session management
 | 
			
		||||
//! - GitEa OAuth integration (conditional)
 | 
			
		||||
 | 
			
		||||
use crate::utils::*;
 | 
			
		||||
use crate::environment::*;
 | 
			
		||||
use tokio_test;
 | 
			
		||||
 | 
			
		||||
/// Test user registration flow
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_user_registration_flow() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing user registration flow");
 | 
			
		||||
 | 
			
		||||
    // Get test persona
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to registration page
 | 
			
		||||
    helper.browser.navigate_to("/register").await?;
 | 
			
		||||
    helper.assert_page_loaded("Register").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify registration form elements exist
 | 
			
		||||
    assert!(helper.browser.element_exists("input[name='email'], input[type='email']").await);
 | 
			
		||||
    assert!(helper.browser.element_exists("input[name='password'], input[type='password']").await);
 | 
			
		||||
    assert!(helper.browser.element_exists("input[name='name'], input[name='full_name']").await);
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("registration_form").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Fill registration form
 | 
			
		||||
    helper.browser.type_text("input[name='email'], input[type='email']", &persona.email).await?;
 | 
			
		||||
    helper.browser.type_text("input[name='password'], input[type='password']", &persona.password).await?;
 | 
			
		||||
    helper.browser.type_text("input[name='name'], input[name='full_name']", &persona.name).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Fill additional fields if they exist
 | 
			
		||||
    if helper.browser.element_exists("input[name='confirm_password']").await {
 | 
			
		||||
        helper.browser.type_text("input[name='confirm_password']", &persona.password).await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("registration_form_filled").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Submit registration
 | 
			
		||||
    helper.browser.click("button[type='submit'], .register-btn, .signup-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Wait for success response or redirect
 | 
			
		||||
    helper.browser.wait_for_element(".success, .dashboard, .registration-success, .alert-success").await?;
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("registration_success").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify user is now authenticated (should be on dashboard or logged in)
 | 
			
		||||
    if helper.browser.get_current_url().await?.contains("dashboard") {
 | 
			
		||||
        helper.assert_user_is_authenticated().await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("User registration flow test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test user login flow
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_user_login_flow() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing user login flow");
 | 
			
		||||
 | 
			
		||||
    // Get test persona (should already exist from registration or test data)
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to login page
 | 
			
		||||
    helper.browser.navigate_to("/login").await?;
 | 
			
		||||
    helper.assert_page_loaded("Login").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify login form elements exist
 | 
			
		||||
    assert!(helper.browser.element_exists("input[name='email'], input[type='email']").await);
 | 
			
		||||
    assert!(helper.browser.element_exists("input[name='password'], input[type='password']").await);
 | 
			
		||||
    assert!(helper.browser.element_exists("button[type='submit'], .login-btn").await);
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("login_form").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test login with valid credentials
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify successful login
 | 
			
		||||
    helper.assert_user_is_authenticated().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify redirect to dashboard or appropriate page
 | 
			
		||||
    let current_url = helper.browser.get_current_url().await?;
 | 
			
		||||
    assert!(current_url.contains("dashboard") || current_url.contains("marketplace"));
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("login_success").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("User login flow test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test user logout flow
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_user_logout_flow() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing user logout flow");
 | 
			
		||||
 | 
			
		||||
    // First login
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    helper.assert_user_is_authenticated().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test logout
 | 
			
		||||
    helper.logout().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify logout success
 | 
			
		||||
    let current_url = helper.browser.get_current_url().await?;
 | 
			
		||||
    assert!(!current_url.contains("dashboard"));
 | 
			
		||||
    
 | 
			
		||||
    // Verify user menu/authentication indicators are gone
 | 
			
		||||
    assert!(!helper.browser.element_exists(".user-menu, .logout-btn").await);
 | 
			
		||||
    
 | 
			
		||||
    // Try to access protected page and verify redirect
 | 
			
		||||
    helper.browser.navigate_to("/dashboard").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Should redirect to login or show unauthorized
 | 
			
		||||
    let final_url = helper.browser.get_current_url().await?;
 | 
			
		||||
    assert!(final_url.contains("login") || final_url.contains("unauthorized") || !final_url.contains("dashboard"));
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("logout_success").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("User logout flow test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test cart migration during login
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_cart_migration_during_login() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing cart migration during login");
 | 
			
		||||
 | 
			
		||||
    // Step 1: Add items to cart as anonymous user
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Try to add item to anonymous cart
 | 
			
		||||
    if helper.browser.element_exists(".add-to-cart, .product-card").await {
 | 
			
		||||
        helper.browser.click(".add-to-cart, .product-card .btn").await?;
 | 
			
		||||
        helper.browser.wait_for_element(".cart-updated, .notification").await.ok();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to anonymous cart and verify items
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    let anonymous_cart_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("anonymous_cart_with_items").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Step 2: Login and verify cart migration
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to authenticated user cart
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify cart items were migrated
 | 
			
		||||
    let authenticated_cart_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
    
 | 
			
		||||
    // Cart should have same or more items (if user already had items)
 | 
			
		||||
    assert!(authenticated_cart_items >= anonymous_cart_items, 
 | 
			
		||||
           "Cart migration failed: anonymous had {}, authenticated has {}", 
 | 
			
		||||
           anonymous_cart_items, authenticated_cart_items);
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("authenticated_cart_after_migration").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Cart migration during login test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test session management and persistence
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_session_management() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing session management and persistence");
 | 
			
		||||
 | 
			
		||||
    // Login
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    helper.assert_user_is_authenticated().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to different pages and verify session persists
 | 
			
		||||
    let test_pages = vec![
 | 
			
		||||
        "/dashboard",
 | 
			
		||||
        "/dashboard/wallet",
 | 
			
		||||
        "/marketplace",
 | 
			
		||||
        "/dashboard/settings",
 | 
			
		||||
    ];
 | 
			
		||||
    
 | 
			
		||||
    for page in test_pages {
 | 
			
		||||
        helper.browser.navigate_to(page).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify still authenticated on each page
 | 
			
		||||
        if page.contains("dashboard") {
 | 
			
		||||
            // For dashboard pages, check for dashboard elements
 | 
			
		||||
            helper.browser.wait_for_element(".dashboard, .user-content").await.ok();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Check that we're not redirected to login
 | 
			
		||||
        let current_url = helper.browser.get_current_url().await?;
 | 
			
		||||
        assert!(!current_url.contains("login"), "Session expired on page {}", page);
 | 
			
		||||
        
 | 
			
		||||
        helper.take_screenshot(&format!("session_check_{}", page.replace("/", "_"))).await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test API authentication status
 | 
			
		||||
    let auth_status = helper.api_client.validate_auth_status().await?;
 | 
			
		||||
    assert!(auth_status.authenticated, "API reports user not authenticated");
 | 
			
		||||
    assert_eq!(auth_status.user_email, Some(persona.email.clone()));
 | 
			
		||||
 | 
			
		||||
    log::info!("Session management test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test login error handling
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_login_error_handling() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing login error handling");
 | 
			
		||||
 | 
			
		||||
    // Navigate to login page
 | 
			
		||||
    helper.browser.navigate_to("/login").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test invalid credentials
 | 
			
		||||
    helper.browser.type_text("input[name='email'], input[type='email']", "invalid@example.com").await?;
 | 
			
		||||
    helper.browser.type_text("input[name='password'], input[type='password']", "wrongpassword").await?;
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("login_invalid_credentials").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Submit login
 | 
			
		||||
    helper.browser.click("button[type='submit'], .login-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Should show error message
 | 
			
		||||
    helper.browser.wait_for_element(".error, .alert-danger, .login-error").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify error message is displayed
 | 
			
		||||
    assert!(helper.browser.element_exists(".error, .alert-danger, .login-error").await);
 | 
			
		||||
    
 | 
			
		||||
    // Verify user is not authenticated
 | 
			
		||||
    assert!(!helper.browser.element_exists(".user-menu, .dashboard-link").await);
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("login_error_displayed").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test empty form submission
 | 
			
		||||
    helper.browser.navigate_to("/login").await?;
 | 
			
		||||
    helper.browser.click("button[type='submit'], .login-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Should show validation errors
 | 
			
		||||
    helper.browser.wait_for_element(".validation-error, .required-field").await.ok();
 | 
			
		||||
    helper.take_screenshot("login_validation_errors").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Login error handling test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test GitEa OAuth integration (conditional on environment)
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_gitea_oauth_integration() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing GitEa OAuth integration");
 | 
			
		||||
 | 
			
		||||
    // Check if GitEa OAuth is enabled
 | 
			
		||||
    if std::env::var("GITEA_CLIENT_ID").is_ok() {
 | 
			
		||||
        // Navigate to login page
 | 
			
		||||
        helper.browser.navigate_to("/login").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Look for GitEa OAuth button
 | 
			
		||||
        if helper.browser.element_exists(".oauth-gitea, .gitea-login, a[href*='auth/gitea']").await {
 | 
			
		||||
            helper.take_screenshot("login_with_gitea_option").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Click GitEa OAuth (don't complete the flow in tests)
 | 
			
		||||
            // Just verify the redirect starts
 | 
			
		||||
            helper.browser.click(".oauth-gitea, .gitea-login, a[href*='auth/gitea']").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Should redirect to GitEa or show OAuth flow
 | 
			
		||||
            let current_url = helper.browser.get_current_url().await?;
 | 
			
		||||
            assert!(current_url.contains("gitea") || current_url.contains("oauth") || current_url.contains("auth"));
 | 
			
		||||
            
 | 
			
		||||
            helper.take_screenshot("gitea_oauth_redirect").await?;
 | 
			
		||||
            
 | 
			
		||||
            log::info!("GitEa OAuth integration verified");
 | 
			
		||||
        } else {
 | 
			
		||||
            log::info!("GitEa OAuth button not found - may not be enabled");
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        log::info!("GitEa OAuth not configured - skipping integration test");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test registration validation
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_registration_validation() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing registration validation");
 | 
			
		||||
 | 
			
		||||
    // Navigate to registration page
 | 
			
		||||
    helper.browser.navigate_to("/register").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test empty form submission
 | 
			
		||||
    helper.browser.click("button[type='submit'], .register-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Should show validation errors
 | 
			
		||||
    helper.browser.wait_for_element(".validation-error, .required-field, .error").await.ok();
 | 
			
		||||
    helper.take_screenshot("registration_validation_errors").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test invalid email format
 | 
			
		||||
    helper.browser.type_text("input[name='email'], input[type='email']", "invalid-email").await?;
 | 
			
		||||
    helper.browser.type_text("input[name='password'], input[type='password']", "short").await?;
 | 
			
		||||
    helper.browser.click("button[type='submit'], .register-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Should show format validation errors
 | 
			
		||||
    helper.browser.wait_for_element(".validation-error, .email-error").await.ok();
 | 
			
		||||
    helper.take_screenshot("registration_format_errors").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test duplicate email (if validation exists)
 | 
			
		||||
    let existing_persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.browser.navigate_to("/register").await?;
 | 
			
		||||
    helper.browser.type_text("input[name='email'], input[type='email']", &existing_persona.email).await?;
 | 
			
		||||
    helper.browser.type_text("input[name='password'], input[type='password']", "validpassword123").await?;
 | 
			
		||||
    helper.browser.type_text("input[name='name'], input[name='full_name']", "Test User").await?;
 | 
			
		||||
    helper.browser.click("button[type='submit'], .register-btn").await?;
 | 
			
		||||
    
 | 
			
		||||
    // May show duplicate email error
 | 
			
		||||
    helper.browser.wait_for_element(".error, .duplicate-email").await.ok();
 | 
			
		||||
    helper.take_screenshot("registration_duplicate_email").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Registration validation test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								tests/tests_archive/ux_suite/flows/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/tests_archive/ux_suite/flows/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
//! UX Test Flows
 | 
			
		||||
//! 
 | 
			
		||||
//! Complete end-to-end user flow tests organized by category
 | 
			
		||||
 | 
			
		||||
pub mod public_access;
 | 
			
		||||
pub mod authentication;
 | 
			
		||||
pub mod shopping;
 | 
			
		||||
pub mod dashboard;
 | 
			
		||||
pub mod provider_workflows;
 | 
			
		||||
pub mod settings;
 | 
			
		||||
 | 
			
		||||
pub use public_access::*;
 | 
			
		||||
pub use authentication::*;
 | 
			
		||||
pub use shopping::*;
 | 
			
		||||
pub use dashboard::*;
 | 
			
		||||
pub use provider_workflows::*;
 | 
			
		||||
pub use settings::*;
 | 
			
		||||
							
								
								
									
										317
									
								
								tests/tests_archive/ux_suite/flows/public_access.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								tests/tests_archive/ux_suite/flows/public_access.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,317 @@
 | 
			
		||||
//! Public Access Tests
 | 
			
		||||
//! 
 | 
			
		||||
//! Tests for functionality available without authentication:
 | 
			
		||||
//! - Information pages (/docs, /privacy, /terms, /about, /contact)
 | 
			
		||||
//! - Anonymous marketplace browsing
 | 
			
		||||
//! - Anonymous cart functionality
 | 
			
		||||
//! - Search and filtering
 | 
			
		||||
 | 
			
		||||
use crate::utils::*;
 | 
			
		||||
use crate::environment::*;
 | 
			
		||||
use tokio_test;
 | 
			
		||||
 | 
			
		||||
/// Test all public information pages are accessible
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_public_information_pages() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing public information pages");
 | 
			
		||||
 | 
			
		||||
    // Test each information page
 | 
			
		||||
    let pages = vec![
 | 
			
		||||
        ("/docs", "Documentation"),
 | 
			
		||||
        ("/privacy", "Privacy"),
 | 
			
		||||
        ("/terms", "Terms"),
 | 
			
		||||
        ("/about", "About"),
 | 
			
		||||
        ("/contact", "Contact"),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    for (url, expected_title_part) in pages {
 | 
			
		||||
        log::info!("Testing page: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        // Navigate to page
 | 
			
		||||
        helper.browser.navigate_to(url).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Assert page loads successfully
 | 
			
		||||
        helper.assert_page_loaded(expected_title_part).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot
 | 
			
		||||
        let page_name = url.trim_start_matches('/');
 | 
			
		||||
        helper.take_screenshot(&format!("public_page_{}", page_name)).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify page contains expected content indicators
 | 
			
		||||
        match url {
 | 
			
		||||
            "/docs" => {
 | 
			
		||||
                assert!(helper.browser.element_exists(".documentation, .docs-content, h1").await);
 | 
			
		||||
            }
 | 
			
		||||
            "/privacy" => {
 | 
			
		||||
                assert!(helper.browser.element_exists(".privacy-policy, .legal-content, h1").await);
 | 
			
		||||
            }
 | 
			
		||||
            "/terms" => {
 | 
			
		||||
                assert!(helper.browser.element_exists(".terms-conditions, .legal-content, h1").await);
 | 
			
		||||
            }
 | 
			
		||||
            "/about" => {
 | 
			
		||||
                assert!(helper.browser.element_exists(".about-content, .marketplace-info, h1").await);
 | 
			
		||||
            }
 | 
			
		||||
            "/contact" => {
 | 
			
		||||
                assert!(helper.browser.element_exists(".contact-info, .contact-form, h1").await);
 | 
			
		||||
            }
 | 
			
		||||
            _ => {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("All public information pages test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test anonymous marketplace browsing
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_anonymous_marketplace_browsing() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing anonymous marketplace browsing");
 | 
			
		||||
 | 
			
		||||
    // Test main marketplace page
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    helper.assert_page_loaded("Marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify marketplace loads without authentication
 | 
			
		||||
    assert!(helper.browser.element_exists(".marketplace, .product-grid, .category-nav").await);
 | 
			
		||||
    helper.take_screenshot("marketplace_anonymous").await?;
 | 
			
		||||
 | 
			
		||||
    // Test each marketplace category
 | 
			
		||||
    let categories = vec![
 | 
			
		||||
        ("/marketplace/compute", "compute resources"),
 | 
			
		||||
        ("/marketplace/3nodes", "3nodes"),
 | 
			
		||||
        ("/marketplace/gateways", "gateways"),
 | 
			
		||||
        ("/marketplace/applications", "applications"),
 | 
			
		||||
        ("/marketplace/services", "services"),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    for (url, category_name) in categories {
 | 
			
		||||
        log::info!("Testing marketplace category: {}", category_name);
 | 
			
		||||
        
 | 
			
		||||
        helper.browser.navigate_to(url).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for category content to load
 | 
			
		||||
        helper.browser.wait_for_element(".category-content, .product-list, .marketplace").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify category-specific elements exist
 | 
			
		||||
        match url {
 | 
			
		||||
            "/marketplace/compute" => {
 | 
			
		||||
                // Should show VM/slice options
 | 
			
		||||
                helper.browser.wait_for_element(".compute-resources, .vm-options, .slice-list").await.ok();
 | 
			
		||||
            }
 | 
			
		||||
            "/marketplace/3nodes" => {
 | 
			
		||||
                // Should show node listings
 | 
			
		||||
                helper.browser.wait_for_element(".node-listings, .server-list, .3node-grid").await.ok();
 | 
			
		||||
            }
 | 
			
		||||
            "/marketplace/gateways" => {
 | 
			
		||||
                // Should show gateway services
 | 
			
		||||
                helper.browser.wait_for_element(".gateway-services, .mycelium-gateways").await.ok();
 | 
			
		||||
            }
 | 
			
		||||
            "/marketplace/applications" => {
 | 
			
		||||
                // Should show app listings
 | 
			
		||||
                helper.browser.wait_for_element(".app-grid, .application-list, .published-apps").await.ok();
 | 
			
		||||
            }
 | 
			
		||||
            "/marketplace/services" => {
 | 
			
		||||
                // Should show service listings
 | 
			
		||||
                helper.browser.wait_for_element(".service-list, .professional-services").await.ok();
 | 
			
		||||
            }
 | 
			
		||||
            _ => {}
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let category_clean = category_name.replace(" ", "_");
 | 
			
		||||
        helper.take_screenshot(&format!("marketplace_{}", category_clean)).await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Anonymous marketplace browsing test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test anonymous cart functionality
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_anonymous_cart_functionality() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing anonymous cart functionality");
 | 
			
		||||
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Try to add item to cart (if products are available)
 | 
			
		||||
    if helper.browser.element_exists(".add-to-cart, .product-card").await {
 | 
			
		||||
        // Click first add to cart button found
 | 
			
		||||
        helper.browser.click(".add-to-cart, .product-card .btn-primary").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Should show some indication of cart update
 | 
			
		||||
        helper.browser.wait_for_element(".cart-updated, .notification, .alert").await.ok();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to anonymous cart
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    helper.assert_page_loaded("Cart").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify cart page shows for non-authenticated users
 | 
			
		||||
    assert!(helper.browser.element_exists(".cart, .cart-content, .shopping-cart").await);
 | 
			
		||||
    helper.take_screenshot("anonymous_cart").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Check for login prompt when trying to checkout
 | 
			
		||||
    if helper.browser.element_exists(".checkout-btn, .proceed-checkout").await {
 | 
			
		||||
        helper.browser.click(".checkout-btn, .proceed-checkout").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Should redirect to login or show login prompt
 | 
			
		||||
        helper.browser.wait_for_element(".login-form, .auth-required, .login-prompt").await.ok();
 | 
			
		||||
        helper.take_screenshot("checkout_login_prompt").await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Anonymous cart functionality test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test marketplace search and filtering
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_marketplace_search_and_filtering() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing marketplace search and filtering");
 | 
			
		||||
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test search functionality
 | 
			
		||||
    if helper.browser.element_exists(".search-input, input[type='search']").await {
 | 
			
		||||
        helper.browser.type_text(".search-input, input[type='search']", "test").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Submit search or wait for auto-search
 | 
			
		||||
        if helper.browser.element_exists(".search-btn, .search-submit").await {
 | 
			
		||||
            helper.browser.click(".search-btn, .search-submit").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Wait for search results
 | 
			
		||||
        helper.browser.wait_for_element(".search-results, .product-grid, .filtered-results").await.ok();
 | 
			
		||||
        helper.take_screenshot("marketplace_search_results").await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test category filtering
 | 
			
		||||
    if helper.browser.element_exists(".category-filter, .filter-category").await {
 | 
			
		||||
        let filters = helper.browser.find_elements(".category-filter option, .filter-btn").await.ok();
 | 
			
		||||
        
 | 
			
		||||
        if let Some(filters) = filters {
 | 
			
		||||
            if filters.len() > 1 {
 | 
			
		||||
                // Click first filter option
 | 
			
		||||
                filters[1].click().await.ok();
 | 
			
		||||
                
 | 
			
		||||
                // Wait for filtered results
 | 
			
		||||
                helper.browser.wait_for_element(".filtered-results, .category-products").await.ok();
 | 
			
		||||
                helper.take_screenshot("marketplace_filtered_results").await?;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test price filtering if available
 | 
			
		||||
    if helper.browser.element_exists(".price-filter, .price-range").await {
 | 
			
		||||
        helper.take_screenshot("marketplace_price_filter").await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Marketplace search and filtering test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test product details pages
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_product_details_pages() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing product details pages");
 | 
			
		||||
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Find and click on a product (if available)
 | 
			
		||||
    if helper.browser.element_exists(".product-card, .product-item").await {
 | 
			
		||||
        helper.browser.click(".product-card, .product-item").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for product details page
 | 
			
		||||
        helper.browser.wait_for_element(".product-details, .product-info, .item-details").await.ok();
 | 
			
		||||
        
 | 
			
		||||
        // Verify product details elements
 | 
			
		||||
        let expected_elements = vec![
 | 
			
		||||
            ".product-name, .item-title",
 | 
			
		||||
            ".product-price, .price-info",
 | 
			
		||||
            ".product-description, .item-description",
 | 
			
		||||
        ];
 | 
			
		||||
        
 | 
			
		||||
        for element in expected_elements {
 | 
			
		||||
            if helper.browser.element_exists(element).await {
 | 
			
		||||
                log::info!("Found product detail element: {}", element);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        helper.take_screenshot("product_details_page").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Test product action buttons (without authentication)
 | 
			
		||||
        if helper.browser.element_exists(".add-to-cart").await {
 | 
			
		||||
            log::info!("Add to cart button available on product details");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if helper.browser.element_exists(".buy-now").await {
 | 
			
		||||
            log::info!("Buy now button available on product details");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Product details pages test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test marketplace accessibility and responsiveness
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_marketplace_accessibility() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing marketplace accessibility and responsiveness");
 | 
			
		||||
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test different viewport sizes
 | 
			
		||||
    let viewport_sizes = vec![
 | 
			
		||||
        (1920, 1080), // Desktop
 | 
			
		||||
        (768, 1024),  // Tablet
 | 
			
		||||
        (375, 667),   // Mobile
 | 
			
		||||
    ];
 | 
			
		||||
    
 | 
			
		||||
    for (width, height) in viewport_sizes {
 | 
			
		||||
        log::info!("Testing viewport size: {}x{}", width, height);
 | 
			
		||||
        
 | 
			
		||||
        // Set viewport size
 | 
			
		||||
        helper.browser.driver.set_window_size(width, height).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for layout to adjust
 | 
			
		||||
        helper.wait(std::time::Duration::from_millis(500)).await;
 | 
			
		||||
        
 | 
			
		||||
        // Take screenshot for visual verification
 | 
			
		||||
        helper.take_screenshot(&format!("marketplace_{}x{}", width, height)).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify key elements are still accessible
 | 
			
		||||
        assert!(helper.browser.element_exists("body").await);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Reset to default size
 | 
			
		||||
    helper.browser.driver.set_window_size(1920, 1080).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Marketplace accessibility test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										445
									
								
								tests/tests_archive/ux_suite/flows/shopping.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								tests/tests_archive/ux_suite/flows/shopping.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,445 @@
 | 
			
		||||
//! Shopping Workflow Tests
 | 
			
		||||
//! 
 | 
			
		||||
//! Tests for complete shopping experience:
 | 
			
		||||
//! - Buy Now functionality
 | 
			
		||||
//! - Add to Cart workflow
 | 
			
		||||
//! - Checkout process
 | 
			
		||||
//! - Order confirmation
 | 
			
		||||
//! - Cart management (add, remove, edit quantities)
 | 
			
		||||
 | 
			
		||||
use crate::utils::*;
 | 
			
		||||
use crate::environment::*;
 | 
			
		||||
use tokio_test;
 | 
			
		||||
 | 
			
		||||
/// Test Buy Now complete workflow
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_buy_now_complete_workflow() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing Buy Now complete workflow");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Look for products with Buy Now option
 | 
			
		||||
    if helper.browser.element_exists(".product-card, .product-item").await {
 | 
			
		||||
        // Get initial wallet balance
 | 
			
		||||
        let initial_balance = helper.api_client.get_wallet_balance().await.ok();
 | 
			
		||||
        
 | 
			
		||||
        // Click on first product to get details
 | 
			
		||||
        helper.browser.click(".product-card, .product-item").await?;
 | 
			
		||||
        helper.browser.wait_for_element(".product-details, .buy-now").await.ok();
 | 
			
		||||
        
 | 
			
		||||
        helper.take_screenshot("product_page_buy_now").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Click Buy Now button
 | 
			
		||||
        if helper.browser.element_exists(".buy-now, button[data-action='buy-now']").await {
 | 
			
		||||
            helper.browser.click(".buy-now, button[data-action='buy-now']").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Complete checkout flow
 | 
			
		||||
            let order_id = helper.complete_checkout_flow().await?;
 | 
			
		||||
            
 | 
			
		||||
            // Verify order confirmation
 | 
			
		||||
            assert!(!order_id.is_empty());
 | 
			
		||||
            helper.assert_element_contains_text(".order-confirmation, .success", "success").await.ok();
 | 
			
		||||
            
 | 
			
		||||
            // Check if wallet balance was deducted (if we got initial balance)
 | 
			
		||||
            if let Some(initial) = initial_balance {
 | 
			
		||||
                let final_balance = helper.api_client.get_wallet_balance().await?;
 | 
			
		||||
                assert!(final_balance.balance <= initial.balance, "Wallet balance should be deducted");
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            helper.take_screenshot("buy_now_success").await?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Buy Now complete workflow test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test Add to Cart workflow
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_add_to_cart_workflow() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing Add to Cart workflow");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to marketplace
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Initial cart should be empty or have known state
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    let initial_cart_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
    
 | 
			
		||||
    // Go back to marketplace and add items
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Add first product to cart
 | 
			
		||||
    if helper.browser.element_exists(".add-to-cart, .product-card").await {
 | 
			
		||||
        helper.browser.click(".add-to-cart, .product-card .btn:not(.buy-now)").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for cart update notification
 | 
			
		||||
        helper.browser.wait_for_element(".cart-updated, .notification, .alert-success").await.ok();
 | 
			
		||||
        helper.take_screenshot("item_added_to_cart").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify cart badge updated
 | 
			
		||||
        if helper.browser.element_exists(".cart-badge, .cart-count").await {
 | 
			
		||||
            let badge_text = helper.browser.get_text(".cart-badge, .cart-count").await?;
 | 
			
		||||
            let cart_count = badge_text.parse::<usize>().unwrap_or(0);
 | 
			
		||||
            assert!(cart_count > initial_cart_items, "Cart count should increase");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to cart and verify item
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    let final_cart_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
    assert!(final_cart_items > initial_cart_items, "Cart should have more items");
 | 
			
		||||
    
 | 
			
		||||
    helper.take_screenshot("cart_with_added_items").await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Add to Cart workflow test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test cart management (edit quantities, remove items)
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_cart_management() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing cart management");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Add items to cart first
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Add multiple items if possible
 | 
			
		||||
    let add_to_cart_buttons = helper.browser.find_elements(".add-to-cart, .product-card .btn").await.unwrap_or_default();
 | 
			
		||||
    for (i, button) in add_to_cart_buttons.iter().take(2).enumerate() {
 | 
			
		||||
        button.click().await.ok();
 | 
			
		||||
        helper.wait(std::time::Duration::from_millis(500)).await;
 | 
			
		||||
        log::info!("Added item {} to cart", i + 1);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to cart
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test quantity editing (if available)
 | 
			
		||||
    if helper.browser.element_exists(".quantity-input, input[name='quantity']").await {
 | 
			
		||||
        helper.browser.type_text(".quantity-input, input[name='quantity']", "3").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Click update quantity button if exists
 | 
			
		||||
        if helper.browser.element_exists(".update-quantity, .quantity-update").await {
 | 
			
		||||
            helper.browser.click(".update-quantity, .quantity-update").await?;
 | 
			
		||||
            helper.browser.wait_for_element(".cart-updated, .notification").await.ok();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        helper.take_screenshot("cart_quantity_updated").await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test increase/decrease quantity buttons
 | 
			
		||||
    if helper.browser.element_exists(".quantity-increase, .btn-plus").await {
 | 
			
		||||
        helper.browser.click(".quantity-increase, .btn-plus").await?;
 | 
			
		||||
        helper.wait(std::time::Duration::from_millis(500)).await;
 | 
			
		||||
        helper.take_screenshot("cart_quantity_increased").await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if helper.browser.element_exists(".quantity-decrease, .btn-minus").await {
 | 
			
		||||
        helper.browser.click(".quantity-decrease, .btn-minus").await?;
 | 
			
		||||
        helper.wait(std::time::Duration::from_millis(500)).await;
 | 
			
		||||
        helper.take_screenshot("cart_quantity_decreased").await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test remove item from cart
 | 
			
		||||
    let initial_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
    
 | 
			
		||||
    if helper.browser.element_exists(".remove-item, .delete-item, .btn-remove").await {
 | 
			
		||||
        helper.browser.click(".remove-item, .delete-item, .btn-remove").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for item removal
 | 
			
		||||
        helper.browser.wait_for_element(".item-removed, .notification").await.ok();
 | 
			
		||||
        
 | 
			
		||||
        // Verify item count decreased
 | 
			
		||||
        let final_items = helper.browser.find_elements(".cart-item, .product-item").await.unwrap_or_default().len();
 | 
			
		||||
        assert!(final_items < initial_items, "Cart items should decrease after removal");
 | 
			
		||||
        
 | 
			
		||||
        helper.take_screenshot("cart_item_removed").await?;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Test clear cart functionality
 | 
			
		||||
    if helper.browser.element_exists(".clear-cart, .empty-cart").await {
 | 
			
		||||
        helper.browser.click(".clear-cart, .empty-cart").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Handle confirmation if present
 | 
			
		||||
        if helper.browser.element_exists(".confirm-clear, .confirm-yes").await {
 | 
			
		||||
            helper.browser.click(".confirm-clear, .confirm-yes").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Verify cart is empty
 | 
			
		||||
        helper.browser.wait_for_element(".cart-empty, .empty-state").await.ok();
 | 
			
		||||
        helper.take_screenshot("cart_cleared").await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Cart management test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test complete checkout process
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_complete_checkout_process() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment);
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing complete checkout process");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Add items to cart
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    if helper.browser.element_exists(".add-to-cart, .product-card").await {
 | 
			
		||||
        helper.browser.click(".add-to-cart, .product-card .btn").await?;
 | 
			
		||||
        helper.browser.wait_for_element(".cart-updated, .notification").await.ok();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to cart
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    helper.take_screenshot("cart_before_checkout").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Get initial wallet balance for verification
 | 
			
		||||
    let initial_balance = helper.api_client.get_wallet_balance().await.ok();
 | 
			
		||||
    
 | 
			
		||||
    // Proceed to checkout
 | 
			
		||||
    if helper.browser.element_exists(".checkout-btn, .proceed-checkout").await {
 | 
			
		||||
        helper.browser.click(".checkout-btn, .proceed-checkout").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for checkout page/modal
 | 
			
		||||
        helper.browser.wait_for_element(".checkout, .checkout-form, .payment-form").await?;
 | 
			
		||||
        helper.take_screenshot("checkout_page").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Fill checkout form if required
 | 
			
		||||
        if helper.browser.element_exists("input[name='billing_address']").await {
 | 
			
		||||
            helper.browser.type_text("input[name='billing_address']", "123 Test Street").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if helper.browser.element_exists("input[name='city']").await {
 | 
			
		||||
            helper.browser.type_text("input[name='city']", "Test City").await?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Confirm purchase
 | 
			
		||||
        if helper.browser.element_exists(".confirm-purchase, .place-order").await {
 | 
			
		||||
            helper.browser.click(".confirm-purchase, .place-order").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Wait for order processing
 | 
			
		||||
            helper.browser.wait_for_element(".processing, .order-confirmation, .success").await?;
 | 
			
		||||
            helper.take_screenshot("order_processing").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Wait for final confirmation
 | 
			
		||||
            helper.browser.wait_for_element(".order-success, .purchase-complete").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Verify order details are displayed
 | 
			
		||||
            if helper.browser.element_exists(".order-id, .order-number").await {
 | 
			
		||||
                let order_id = helper.browser.get_text(".order-id, .order-number").await?;
 | 
			
		||||
                assert!(!order_id.is_empty(), "Order ID should be displayed");
 | 
			
		||||
                log::info!("Order completed with ID: {}", order_id);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            helper.take_screenshot("checkout_success").await?;
 | 
			
		||||
            
 | 
			
		||||
            // Verify wallet balance was deducted
 | 
			
		||||
            if let Some(initial) = initial_balance {
 | 
			
		||||
                let final_balance = helper.api_client.get_wallet_balance().await?;
 | 
			
		||||
                assert!(final_balance.balance < initial.balance, "Wallet balance should be deducted");
 | 
			
		||||
                log::info!("Wallet balance: {} -> {}", initial.balance, final_balance.balance);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Complete checkout process test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test checkout with insufficient funds
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_checkout_insufficient_funds() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing checkout with insufficient funds");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Check current wallet balance
 | 
			
		||||
    let balance = helper.api_client.get_wallet_balance().await?;
 | 
			
		||||
    
 | 
			
		||||
    // If balance is sufficient, we can't test insufficient funds scenario easily
 | 
			
		||||
    // This test would need a way to set balance to insufficient amount
 | 
			
		||||
    log::info!("Current balance: {} {}", balance.balance, balance.currency);
 | 
			
		||||
    
 | 
			
		||||
    // Add expensive item to cart (if available) or multiple items
 | 
			
		||||
    helper.browser.navigate_to("/marketplace").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Add items to cart until we might exceed balance
 | 
			
		||||
    let products = helper.browser.find_elements(".product-card, .add-to-cart").await.unwrap_or_default();
 | 
			
		||||
    for product in products.iter().take(5) {
 | 
			
		||||
        product.click().await.ok();
 | 
			
		||||
        helper.wait(std::time::Duration::from_millis(300)).await;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to cart and try checkout
 | 
			
		||||
    helper.navigate_to_cart().await?;
 | 
			
		||||
    
 | 
			
		||||
    if helper.browser.element_exists(".checkout-btn").await {
 | 
			
		||||
        helper.browser.click(".checkout-btn").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Look for insufficient funds error
 | 
			
		||||
        helper.browser.wait_for_element(".insufficient-funds, .payment-error, .error").await.ok();
 | 
			
		||||
        
 | 
			
		||||
        if helper.browser.element_exists(".insufficient-funds, .payment-error").await {
 | 
			
		||||
            helper.take_screenshot("insufficient_funds_error").await?;
 | 
			
		||||
            log::info!("Insufficient funds error properly displayed");
 | 
			
		||||
        } else {
 | 
			
		||||
            log::info!("No insufficient funds scenario encountered");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test order history and tracking
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_order_history_and_tracking() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing order history and tracking");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Navigate to order history
 | 
			
		||||
    helper.navigate_to_dashboard_section("orders").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Verify orders page elements
 | 
			
		||||
    assert!(helper.browser.element_exists(".orders, .order-history, .order-list").await);
 | 
			
		||||
    helper.take_screenshot("order_history_page").await?;
 | 
			
		||||
    
 | 
			
		||||
    // Check if orders exist
 | 
			
		||||
    if helper.browser.element_exists(".order-item, .order-row").await {
 | 
			
		||||
        // Click on first order for details
 | 
			
		||||
        helper.browser.click(".order-item, .order-row").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for order details
 | 
			
		||||
        helper.browser.wait_for_element(".order-details, .order-info").await.ok();
 | 
			
		||||
        helper.take_screenshot("order_details_page").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Verify order details elements
 | 
			
		||||
        let expected_elements = vec![
 | 
			
		||||
            ".order-id, .order-number",
 | 
			
		||||
            ".order-date, .purchase-date",
 | 
			
		||||
            ".order-status, .status",
 | 
			
		||||
            ".order-total, .total-amount",
 | 
			
		||||
        ];
 | 
			
		||||
        
 | 
			
		||||
        for element in expected_elements {
 | 
			
		||||
            if helper.browser.element_exists(element).await {
 | 
			
		||||
                log::info!("Found order detail element: {}", element);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Test invoice download if available
 | 
			
		||||
        if helper.browser.element_exists(".download-invoice, .invoice-link").await {
 | 
			
		||||
            helper.browser.click(".download-invoice, .invoice-link").await?;
 | 
			
		||||
            helper.take_screenshot("invoice_download").await?;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        log::info!("No orders found in history - this is expected for new test environment");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Order history and tracking test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test different product categories checkout
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
#[serial_test::serial]
 | 
			
		||||
async fn test_different_product_categories_checkout() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let environment = TestFixtures::setup_test_environment().await?;
 | 
			
		||||
    let mut helper = UXTestHelper::new(&environment).await?;
 | 
			
		||||
 | 
			
		||||
    log::info!("Testing different product categories checkout");
 | 
			
		||||
 | 
			
		||||
    // Login as consumer
 | 
			
		||||
    let persona = helper.data_manager.get_persona(&UserRole::Consumer).unwrap();
 | 
			
		||||
    helper.login_as(persona).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Test different marketplace categories
 | 
			
		||||
    let categories = vec![
 | 
			
		||||
        "/marketplace/compute",
 | 
			
		||||
        "/marketplace/applications", 
 | 
			
		||||
        "/marketplace/services",
 | 
			
		||||
    ];
 | 
			
		||||
    
 | 
			
		||||
    for category_url in categories {
 | 
			
		||||
        log::info!("Testing checkout for category: {}", category_url);
 | 
			
		||||
        
 | 
			
		||||
        helper.browser.navigate_to(category_url).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Look for products in this category
 | 
			
		||||
        if helper.browser.element_exists(".product-card, .service-item, .app-item").await {
 | 
			
		||||
            // Try to add item to cart
 | 
			
		||||
            helper.browser.click(".add-to-cart, .buy-now").await.ok();
 | 
			
		||||
            
 | 
			
		||||
            // Handle any category-specific checkout flows
 | 
			
		||||
            match category_url {
 | 
			
		||||
                "/marketplace/compute" => {
 | 
			
		||||
                    // VM/Compute might have configuration options
 | 
			
		||||
                    if helper.browser.element_exists(".vm-config, .compute-options").await {
 | 
			
		||||
                        helper.take_screenshot("compute_configuration").await?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "/marketplace/applications" => {
 | 
			
		||||
                    // Apps might have deployment options
 | 
			
		||||
                    if helper.browser.element_exists(".app-config, .deployment-options").await {
 | 
			
		||||
                        helper.take_screenshot("app_configuration").await?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "/marketplace/services" => {
 | 
			
		||||
                    // Services might have booking/scheduling
 | 
			
		||||
                    if helper.browser.element_exists(".service-booking, .schedule-options").await {
 | 
			
		||||
                        helper.take_screenshot("service_booking").await?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                _ => {}
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            helper.wait(std::time::Duration::from_millis(500)).await;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log::info!("Different product categories checkout test completed successfully");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								tests/tests_archive/ux_suite/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/tests_archive/ux_suite/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
//! Project Mycelium UX Test Suite
 | 
			
		||||
//! 
 | 
			
		||||
//! Complete end-to-end testing framework for validating the entire user experience
 | 
			
		||||
//! as specified in the roadmap Section 9.
 | 
			
		||||
//!
 | 
			
		||||
//! ## Test Categories:
 | 
			
		||||
//! - Public Access Tests (information pages, anonymous marketplace browsing)
 | 
			
		||||
//! - Authentication Tests (registration, login, cart migration)
 | 
			
		||||
//! - Shopping Tests (buy now, add to cart, checkout flows)
 | 
			
		||||
//! - Dashboard Tests (user dashboard, wallet, orders, settings)
 | 
			
		||||
//! - Provider Tests (farmer, app provider, service provider workflows)
 | 
			
		||||
//! - Advanced Features (SSH keys, VM/Kubernetes, modal workflows)
 | 
			
		||||
 | 
			
		||||
pub mod environment;
 | 
			
		||||
pub mod fixtures;
 | 
			
		||||
pub mod flows;
 | 
			
		||||
pub mod utils;
 | 
			
		||||
pub mod reporting;
 | 
			
		||||
 | 
			
		||||
pub use environment::*;
 | 
			
		||||
pub use utils::*;
 | 
			
		||||
							
								
								
									
										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