diff --git a/bulk_test_cmd b/bulk_test_cmd index 4b90890..718701a 100644 --- a/bulk_test_cmd +++ b/bulk_test_cmd @@ -8,4 +8,17 @@ python3 tests/load_tests/test_generic_load_assessments.py \ --start 0 --end 1 \ --workers 1 \ --metrics-interval 1 \ - --headless \ No newline at end of file + --headless + + + +PC 1 - 60 Students (20 from each CSV) +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & wait +PC 2 - 60 Students (20 from each CSV) +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & wait +PC 3 - 60 Students (20 from each CSV) +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & wait +PC 4 - 60 Students (20 from each CSV) +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & wait +PC 5 - 60 Students (20 from each CSV) +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & wait diff --git a/pages/base_page.py b/pages/base_page.py index 54e23b5..98af5e4 100644 --- a/pages/base_page.py +++ b/pages/base_page.py @@ -34,7 +34,8 @@ class BasePage: self.wait_for_page_load() def wait_for_page_load(self): - """Wait for page to load completely""" + """Wait for page to load completely (machine-speed optimized)""" + # Machine-speed: Both methods now return immediately if already ready self.wait.wait_for_page_load() self.wait.wait_for_loading_to_disappear() diff --git a/pages/domain_assessment_page.py b/pages/domain_assessment_page.py index 949f536..a5ec616 100644 --- a/pages/domain_assessment_page.py +++ b/pages/domain_assessment_page.py @@ -234,9 +234,9 @@ class DomainAssessmentPage(BasePage): self.wait_for_page_load() def click_next(self): - """Click Next button""" + """Click Next button (machine-speed optimized)""" self.click_element(self.NEXT_BUTTON) - # Wait for next question to load + # Machine-speed: Minimal wait - page load check returns immediately if already loaded self.wait_for_page_load() def click_submit(self): diff --git a/scripts/5_PC_LOAD_TEST_COMMANDS.md b/scripts/5_PC_LOAD_TEST_COMMANDS.md new file mode 100644 index 0000000..ff234f3 --- /dev/null +++ b/scripts/5_PC_LOAD_TEST_COMMANDS.md @@ -0,0 +1,132 @@ +# 5 PC Load Test Commands - Complete Distribution + +## ๐ŸŽฏ Overview + +All 3 CSVs have 100 students each (300 total). Distributed across 5 PCs: +- **Each PC**: 20 students from CSV 1 + 20 from CSV 2 + 20 from CSV 3 = **60 students per PC** +- **Total**: 300 students across 5 PCs +- **Concurrent browsers**: 30 per PC (10 per CSV batch) = **150 total browsers** + +--- + +## ๐Ÿ“Š Distribution Plan + +| PC | CSV 1 | CSV 2 | CSV 3 | Total Students | Workers | +|----|-------|-------|-------|----------------|---------| +| PC 1 | 0-19 (20) | 0-19 (20) | 0-19 (20) | 60 | 30 (10ร—3) | +| PC 2 | 20-39 (20) | 20-39 (20) | 20-39 (20) | 60 | 30 (10ร—3) | +| PC 3 | 40-59 (20) | 40-59 (20) | 40-59 (20) | 60 | 30 (10ร—3) | +| PC 4 | 60-79 (20) | 60-79 (20) | 60-79 (20) | 60 | 30 (10ร—3) | +| PC 5 | 80-99 (20) | 80-99 (20) | 80-99 (20) | 60 | 30 (10ร—3) | +| **Total** | **100** | **100** | **100** | **300** | **150** | + +--- + +## ๐Ÿš€ Complete Commands (Copy-Paste Ready) + +### **PC 1 - 60 Students (20 from each CSV)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 & wait +``` + +### **PC 2 - 60 Students (20 from each CSV)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 20 --end 40 --workers 10 --headless --metrics-interval 5 & wait +``` + +### **PC 3 - 60 Students (20 from each CSV)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 40 --end 60 --workers 10 --headless --metrics-interval 5 & wait +``` + +### **PC 4 - 60 Students (20 from each CSV)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 60 --end 80 --workers 10 --headless --metrics-interval 5 & wait +``` + +### **PC 5 - 60 Students (20 from each CSV)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 80 --end 100 --workers 10 --headless --metrics-interval 5 & wait +``` + +--- + +## ๐Ÿ“ Using Shell Scripts (Recommended) + +### **On PC 1:** +```bash +chmod +x scripts/PC1_60_students.sh +./scripts/PC1_60_students.sh +``` + +### **On PC 2:** +```bash +chmod +x scripts/PC2_60_students.sh +./scripts/PC2_60_students.sh +``` + +### **On PC 3:** +```bash +chmod +x scripts/PC3_60_students.sh +./scripts/PC3_60_students.sh +``` + +### **On PC 4:** +```bash +chmod +x scripts/PC4_60_students.sh +./scripts/PC4_60_students.sh +``` + +### **On PC 5:** +```bash +chmod +x scripts/PC5_60_students.sh +./scripts/PC5_60_students.sh +``` + +--- + +## ๐ŸŒ With Custom URL + +Add `--url` to each command in the script, or modify the shell scripts: + +**Example for PC 1 with custom URL:** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 --url http://localhost:3983 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 --url http://localhost:3983 & python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 20 --workers 10 --headless --metrics-interval 5 --url http://localhost:3983 & wait +``` + +--- + +## ๐Ÿ“Š Summary + +- **Total Students**: 300 (100 from each CSV) +- **Total PCs**: 5 +- **Students per PC**: 60 (20 from each CSV) +- **Concurrent Browsers per PC**: 30 (10 per CSV batch) +- **Total Concurrent Browsers**: 150 +- **Distribution**: Evenly distributed across all CSVs and PCs + +--- + +## ๐Ÿš€ Execution Steps + +1. **On PC 1**: Run PC 1 command/script +2. **On PC 2**: Run PC 2 command/script +3. **On PC 3**: Run PC 3 command/script +4. **On PC 4**: Run PC 4 command/script +5. **On PC 5**: Run PC 5 command/script +6. **Monitor**: Each PC will run 3 parallel processes (one per CSV) + +--- + +## ๐Ÿ’ก Notes + +- Each PC runs **3 parallel processes** (one for each CSV) +- Each process handles **20 students** with **10 workers** +- All processes run in **background** and wait for completion +- **Metrics** printed every 5 students per process +- **Reports** saved separately for each CSV batch + +--- + +**Status**: โœ… Ready to Execute - 300 Students Across 5 PCs + diff --git a/scripts/6_PC_LOAD_TEST_COMMANDS.md b/scripts/6_PC_LOAD_TEST_COMMANDS.md new file mode 100644 index 0000000..210cb29 --- /dev/null +++ b/scripts/6_PC_LOAD_TEST_COMMANDS.md @@ -0,0 +1,93 @@ +# 6 PC Load Test Commands - 50 Students Each + +## ๐ŸŽฏ Overview + +6 PCs, each running 50 students simultaneously. Total: 300 students (100 from each CSV). + +--- + +## ๐Ÿš€ Complete Commands (Copy-Paste Ready) + +### **PC 1 - 50 Students from CSV 1 (indices 0-49)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 50 --workers 50 --headless --metrics-interval 10 +``` + +### **PC 2 - 50 Students from CSV 1 (indices 50-99)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 50 --end 100 --workers 50 --headless --metrics-interval 10 +``` + +### **PC 3 - 50 Students from CSV 2 (indices 0-49)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 50 --workers 50 --headless --metrics-interval 10 +``` + +### **PC 4 - 50 Students from CSV 2 (indices 50-99)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 50 --end 100 --workers 50 --headless --metrics-interval 10 +``` + +### **PC 5 - 50 Students from CSV 3 (indices 0-49)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 50 --workers 50 --headless --metrics-interval 10 +``` + +### **PC 6 - 50 Students from CSV 3 (indices 50-99)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 50 --end 100 --workers 50 --headless --metrics-interval 10 +``` + +--- + +## ๐Ÿ“Š Distribution + +| PC | CSV File | Students | Indices | Workers | +|----|----------|----------|---------|---------| +| PC 1 | CSV 1 | 50 | 0-49 | 50 | +| PC 2 | CSV 1 | 50 | 50-99 | 50 | +| PC 3 | CSV 2 | 50 | 0-49 | 50 | +| PC 4 | CSV 2 | 50 | 50-99 | 50 | +| PC 5 | CSV 3 | 50 | 0-49 | 50 | +| PC 6 | CSV 3 | 50 | 50-99 | 50 | +| **Total** | **3 CSVs** | **300** | **-** | **300** | + +--- + +## ๐Ÿ“ Using Shell Scripts + +**On each PC:** +```bash +# PC 1 +./scripts/PC1_50_students.sh + +# PC 2 +./scripts/PC2_50_students.sh + +# PC 3 +./scripts/PC3_50_students.sh + +# PC 4 +./scripts/PC4_50_students.sh + +# PC 5 +./scripts/PC5_50_students.sh + +# PC 6 +./scripts/PC6_50_students.sh +``` + +--- + +## ๐ŸŒ With Custom URL + +Add `--url` to any command: + +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 50 --workers 50 --headless --metrics-interval 10 --url http://localhost:3983 +``` + +--- + +**Status**: โœ… Ready - 6 PCs, 50 Students Each, All Running Simultaneously + diff --git a/scripts/MULTI_SYSTEM_COMMANDS.md b/scripts/MULTI_SYSTEM_COMMANDS.md new file mode 100644 index 0000000..fcf3981 --- /dev/null +++ b/scripts/MULTI_SYSTEM_COMMANDS.md @@ -0,0 +1,132 @@ +# Multi-System Load Test Commands + +## ๐ŸŽฏ Overview + +Complete commands to run load tests across multiple systems, with each system handling 50 students from different CSV files. + +--- + +## ๐Ÿ“‹ Available CSV Files + +1. `students_with_passwords_2025-12-15T10-49-08_01.csv` +2. `students_with_passwords_2025-12-15T10-59-02_03.csv` +3. `students_with_passwords_2025-12-15T11-06-37_05.csv` + +--- + +## ๐Ÿš€ Complete Commands (Copy-Paste Ready) + +### **SYSTEM 1 - PC 1** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 +``` + +### **SYSTEM 2 - PC 2** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 +``` + +### **SYSTEM 3 - PC 3** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 +``` + +--- + +## ๐ŸŒ With Custom URL + +If you need to specify a custom frontend URL, add `--url` argument: + +### **SYSTEM 1 - PC 1 (with custom URL)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-49-08_01.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 --url http://localhost:3983 +``` + +### **SYSTEM 2 - PC 2 (with custom URL)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T10-59-02_03.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 --url http://localhost:3983 +``` + +### **SYSTEM 3 - PC 3 (with custom URL)** +```bash +cd /home/tech4biz/work/CP_Front_Automation_Test && source venv/bin/activate && python3 tests/load_tests/test_generic_load_assessments.py --csv students_with_passwords_2025-12-15T11-06-37_05.csv --start 0 --end 50 --workers 20 --headless --metrics-interval 10 --url http://localhost:3983 +``` + +--- + +## ๐Ÿ“ Using Shell Scripts + +Alternatively, use the provided shell scripts: + +### **On PC 1:** +```bash +chmod +x scripts/PC1_50_students.sh +./scripts/PC1_50_students.sh +``` + +### **On PC 2:** +```bash +chmod +x scripts/PC2_50_students.sh +./scripts/PC2_50_students.sh +``` + +### **On PC 3:** +```bash +chmod +x scripts/PC3_50_students.sh +./scripts/PC3_50_students.sh +``` + +--- + +## ๐Ÿ“Š Configuration Summary + +| System | CSV File | Students | Indices | Workers | Total Load | +|--------|----------|----------|---------|---------|------------| +| PC 1 | `2025-12-15T10-49-08_01.csv` | 50 | 0-49 | 20 | 50 students | +| PC 2 | `2025-12-15T10-59-02_03.csv` | 50 | 0-49 | 20 | 50 students | +| PC 3 | `2025-12-15T11-06-37_05.csv` | 50 | 0-49 | 20 | 50 students | +| **Total** | **3 CSVs** | **150** | **-** | **60** | **150 students** | + +--- + +## ๐Ÿ”ง Command Breakdown + +- `--csv`: CSV file path +- `--start 0`: Start from first student (index 0) +- `--end 50`: End at 50th student (exclusive, so indices 0-49) +- `--workers 20`: 20 concurrent browsers per system +- `--headless`: Run in headless mode +- `--metrics-interval 10`: Print metrics every 10 students +- `--url` (optional): Custom frontend URL + +--- + +## ๐Ÿš€ Execution Steps + +1. **On PC 1**: Run the PC 1 command +2. **On PC 2**: Run the PC 2 command +3. **On PC 3**: Run the PC 3 command +4. **Monitor**: Each system will print real-time metrics + +--- + +## ๐Ÿ“ˆ Expected Results + +- **Total Students**: 150 (50 per system) +- **Concurrent Browsers**: 60 total (20 per system) +- **Load Distribution**: Evenly distributed across 3 systems +- **Execution Time**: ~20-40 minutes per system (depending on performance) + +--- + +## ๐Ÿ’ก Tips + +1. **Start all systems simultaneously** for maximum load +2. **Monitor each system** for real-time metrics +3. **Check reports** in `reports/load_tests/` after completion +4. **Use `--url`** if testing against different environments + +--- + +**Status**: โœ… Ready to Execute + diff --git a/scripts/MULTI_SYSTEM_LOAD_TEST.sh b/scripts/MULTI_SYSTEM_LOAD_TEST.sh new file mode 100755 index 0000000..b5d309f --- /dev/null +++ b/scripts/MULTI_SYSTEM_LOAD_TEST.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Multi-System Load Test Commands +# Each system takes 50 students from different CSVs +# Run these commands on respective systems + +# ============================================================================ +# SYSTEM 1 - PC 1 +# ============================================================================ +# 50 students from first CSV (indices 0-49) +# Command to run on PC 1: + +cd /home/tech4biz/work/CP_Front_Automation_Test && \ +source venv/bin/activate && \ +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 50 \ + --workers 20 \ + --headless \ + --metrics-interval 10 + +# ============================================================================ +# SYSTEM 2 - PC 2 +# ============================================================================ +# 50 students from second CSV (indices 0-49) +# Command to run on PC 2: + +cd /home/tech4biz/work/CP_Front_Automation_Test && \ +source venv/bin/activate && \ +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 0 --end 50 \ + --workers 20 \ + --headless \ + --metrics-interval 10 + +# ============================================================================ +# SYSTEM 3 - PC 3 +# ============================================================================ +# 50 students from third CSV (indices 0-49) +# Command to run on PC 3: + +cd /home/tech4biz/work/CP_Front_Automation_Test && \ +source venv/bin/activate && \ +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 0 --end 50 \ + --workers 20 \ + --headless \ + --metrics-interval 10 + +# ============================================================================ +# WITH CUSTOM URL (if needed) +# ============================================================================ +# Add --url argument to use custom frontend URL +# Example for PC 1 with custom URL: + +# cd /home/tech4biz/work/CP_Front_Automation_Test && \ +# source venv/bin/activate && \ +# python3 tests/load_tests/test_generic_load_assessments.py \ +# --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ +# --start 0 --end 50 \ +# --workers 20 \ +# --headless \ +# --metrics-interval 10 \ +# --url http://localhost:3983 + diff --git a/scripts/PC1_50_students.sh b/scripts/PC1_50_students.sh new file mode 100755 index 0000000..4d106d1 --- /dev/null +++ b/scripts/PC1_50_students.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# PC 1 - 50 students from CSV 1 (indices 0-49) +# Run this on PC 1 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 50 \ + --workers 50 \ + --headless \ + --metrics-interval 10 diff --git a/scripts/PC1_60_students.sh b/scripts/PC1_60_students.sh new file mode 100755 index 0000000..62e9614 --- /dev/null +++ b/scripts/PC1_60_students.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# PC 1 - Load Test Command +# 20 students from each CSV (60 total) +# CSV 1: indices 0-19, CSV 2: indices 0-19, CSV 3: indices 0-19 +# Run this on PC 1 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +# Run CSV 1 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 20 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 2 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 0 --end 20 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 3 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 0 --end 20 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Wait for all background processes +wait + diff --git a/scripts/PC2_50_students.sh b/scripts/PC2_50_students.sh new file mode 100755 index 0000000..b556c4e --- /dev/null +++ b/scripts/PC2_50_students.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# PC 2 - 50 students from CSV 1 (indices 50-99) +# Run this on PC 2 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 50 --end 100 \ + --workers 50 \ + --headless \ + --metrics-interval 10 diff --git a/scripts/PC2_60_students.sh b/scripts/PC2_60_students.sh new file mode 100755 index 0000000..b8ed06d --- /dev/null +++ b/scripts/PC2_60_students.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# PC 2 - Load Test Command +# 20 students from each CSV (60 total) +# CSV 1: indices 20-39, CSV 2: indices 20-39, CSV 3: indices 20-39 +# Run this on PC 2 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +# Run CSV 1 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 20 --end 40 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 2 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 20 --end 40 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 3 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 20 --end 40 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Wait for all background processes +wait + diff --git a/scripts/PC3_50_students.sh b/scripts/PC3_50_students.sh new file mode 100755 index 0000000..cc0f3c8 --- /dev/null +++ b/scripts/PC3_50_students.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# PC 3 - 50 students from CSV 2 (indices 0-49) +# Run this on PC 3 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 0 --end 50 \ + --workers 50 \ + --headless \ + --metrics-interval 10 diff --git a/scripts/PC3_60_students.sh b/scripts/PC3_60_students.sh new file mode 100755 index 0000000..e92d5da --- /dev/null +++ b/scripts/PC3_60_students.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# PC 3 - Load Test Command +# 20 students from each CSV (60 total) +# CSV 1: indices 40-59, CSV 2: indices 40-59, CSV 3: indices 40-59 +# Run this on PC 3 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +# Run CSV 1 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 40 --end 60 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 2 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 40 --end 60 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 3 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 40 --end 60 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Wait for all background processes +wait + diff --git a/scripts/PC4_50_students.sh b/scripts/PC4_50_students.sh new file mode 100755 index 0000000..19b699c --- /dev/null +++ b/scripts/PC4_50_students.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# PC 4 - 50 students from CSV 2 (indices 50-99) +# Run this on PC 4 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 50 --end 100 \ + --workers 50 \ + --headless \ + --metrics-interval 10 + diff --git a/scripts/PC4_60_students.sh b/scripts/PC4_60_students.sh new file mode 100755 index 0000000..60dfea9 --- /dev/null +++ b/scripts/PC4_60_students.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# PC 4 - Load Test Command +# 20 students from each CSV (60 total) +# CSV 1: indices 60-79, CSV 2: indices 60-79, CSV 3: indices 60-79 +# Run this on PC 4 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +# Run CSV 1 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 60 --end 80 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 2 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 60 --end 80 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 3 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 60 --end 80 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Wait for all background processes +wait + diff --git a/scripts/PC5_50_students.sh b/scripts/PC5_50_students.sh new file mode 100755 index 0000000..111f0bd --- /dev/null +++ b/scripts/PC5_50_students.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# PC 5 - 50 students from CSV 3 (indices 0-49) +# Run this on PC 5 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 0 --end 50 \ + --workers 50 \ + --headless \ + --metrics-interval 10 + diff --git a/scripts/PC5_60_students.sh b/scripts/PC5_60_students.sh new file mode 100755 index 0000000..c3efca5 --- /dev/null +++ b/scripts/PC5_60_students.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# PC 5 - Load Test Command +# 20 students from each CSV (60 total) +# CSV 1: indices 80-99, CSV 2: indices 80-99, CSV 3: indices 80-99 +# Run this on PC 5 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +# Run CSV 1 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 80 --end 100 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 2 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-59-02_03.csv \ + --start 80 --end 100 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Run CSV 3 (20 students) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 80 --end 100 \ + --workers 10 \ + --headless \ + --metrics-interval 5 & + +# Wait for all background processes +wait + diff --git a/scripts/PC6_50_students.sh b/scripts/PC6_50_students.sh new file mode 100755 index 0000000..339afaa --- /dev/null +++ b/scripts/PC6_50_students.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# PC 6 - 50 students from CSV 3 (indices 50-99) +# Run this on PC 6 + +cd /home/tech4biz/work/CP_Front_Automation_Test +source venv/bin/activate + +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T11-06-37_05.csv \ + --start 50 --end 100 \ + --workers 50 \ + --headless \ + --metrics-interval 10 + diff --git a/tests/load_tests/CUSTOM_URL_USAGE.md b/tests/load_tests/CUSTOM_URL_USAGE.md new file mode 100644 index 0000000..35bc818 --- /dev/null +++ b/tests/load_tests/CUSTOM_URL_USAGE.md @@ -0,0 +1,138 @@ +# Custom URL Usage Guide + +## ๐ŸŽฏ Overview + +The load test script now supports a `--url` argument to use any frontend URL dynamically. + +--- + +## ๐Ÿ“ Usage + +### Basic Usage + +```bash +# Use default URL from config (localhost:3983 or live) +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless +``` + +### Custom URL Usage + +```bash +# Use custom local URL +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless \ + --url http://localhost:3983 + +# Use custom port +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless \ + --url http://localhost:5000 + +# Use live/staging URL +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless \ + --url https://cognitiveprism.tech4bizsolutions.com + +# Use any custom URL +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 100 --workers 30 --headless \ + --url https://staging.example.com +``` + +--- + +## ๐Ÿ”ง How It Works + +1. **URL Override**: When `--url` is provided, it overrides `BASE_URL` in `config.config` +2. **Derived URLs**: Automatically updates: + - `LOGIN_URL`: `{url}/` + - `DASHBOARD_URL`: `{url}/student/dashboard` + - `ASSESSMENTS_URL`: `{url}/assessments` + - `PROFILE_EDITOR_URL`: `{url}/student/profile-builder` +3. **Page Objects**: All page objects use the updated URLs when instantiated + +--- + +## ๐Ÿ“‹ Examples + +### Example 1: Local Development +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students.csv \ + --start 0 --end 10 \ + --workers 5 \ + --headless \ + --url http://localhost:3983 +``` + +### Example 2: Staging Environment +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students.csv \ + --start 0 --end 100 \ + --workers 30 \ + --headless \ + --url https://staging.cognitiveprism.com +``` + +### Example 3: Production (with visible browsers for debugging) +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students.csv \ + --start 0 --end 1 \ + --workers 1 \ + --visible \ + --url https://cognitiveprism.tech4bizsolutions.com +``` + +--- + +## โš™๏ธ URL Format + +- **With protocol**: `http://localhost:3983` or `https://example.com` +- **Trailing slash**: Automatically removed (both `http://localhost:3983/` and `http://localhost:3983` work) +- **Port**: Can specify any port (e.g., `http://localhost:5000`) + +--- + +## ๐Ÿ” Verification + +When you run the script, it will print: +- `๐ŸŒ Using custom URL: {your_url}` if `--url` is provided +- `๐ŸŒ Using default URL: {default_url}` if `--url` is not provided + +--- + +## ๐Ÿ“ Notes + +- The URL override happens **before** page objects are instantiated +- All page objects will use the custom URL automatically +- No need to modify config files or environment variables +- Works with all existing functionality (login, password reset, profile, assessments, etc.) + +--- + +## ๐Ÿš€ Quick Reference + +```bash +# Full command with all options +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv \ + --start \ + --end \ + --workers \ + --headless \ + --url \ + --metrics-interval +``` + +--- + +**Status**: โœ… Ready to Use + diff --git a/tests/load_tests/MACHINE_SPEED_OPTIMIZATION.md b/tests/load_tests/MACHINE_SPEED_OPTIMIZATION.md new file mode 100644 index 0000000..1f99296 --- /dev/null +++ b/tests/load_tests/MACHINE_SPEED_OPTIMIZATION.md @@ -0,0 +1,189 @@ +# Machine-Speed Optimization - World Class Automation + +## ๐Ÿš€ Objective + +Transform the automation from **human-like behavior** to **machine-speed execution** while maintaining 100% reliability. + +--- + +## โœ… Optimizations Applied + +### 1. **Removed Human-Like Delays** (Major Impact) +- **Removed**: `RandomizedWait.wait_for_question_answer()` - was adding 2-6 seconds +- **Replaced**: Minimal 0.1s wait for click to register +- **Savings**: ~2-6 seconds per question + +### 2. **Smart Page Load Check** (Major Impact) +- **Optimized**: `wait_for_page_load()` now checks if page is already loaded +- **Returns immediately** if `document.readyState == "complete"` +- **Reduced timeout**: 15s โ†’ 3s (with early return) +- **Savings**: ~10-12 seconds per question (when page already loaded) + +### 3. **Minimal Loading Indicator Wait** (Medium Impact) +- **Reduced**: `wait_for_loading_to_disappear()` timeout: 2s โ†’ 0.5s +- **Reason**: Loading indicators disappear instantly or don't exist +- **Savings**: ~1.5 seconds per question + +### 4. **Fast Question Type Detection** (Medium Impact) +- **Reduced**: All question detection timeouts: 2-3s โ†’ 0.5s +- **Optimized**: Smart wait returns immediately if element already visible +- **Savings**: ~1-2 seconds per question + +### 5. **Reduced Element Wait Timeouts** (Small Impact) +- **Reduced**: All `WebDriverWait` timeouts: 10s โ†’ 2s +- **Reason**: Elements should be ready quickly after page load +- **Savings**: ~0.5-1 second per question (when elements are ready) + +--- + +## ๐Ÿ“Š Performance Comparison + +### Before Optimization: +- **Per Question**: ~25 seconds (observed) +- **100 Questions**: ~42 minutes (observed) +- **Wait Strategy**: Human-like, realistic delays + +### After Machine-Speed Optimization: +- **Per Question**: ~3-8 seconds (estimated) +- **100 Questions**: ~5-13 minutes (estimated) +- **Wait Strategy**: Machine-speed, minimal delays +- **Improvement**: **70-80% faster** (30+ minutes saved!) + +### Breakdown (Machine-Speed): +1. Get question type: 0.1-0.5s (was 0.5-3s) +2. Answer question: 0.5-2s (was 2-6s) +3. Click registration: 0.1s (was 2-6s) +4. Click Next + page load: 0.5-2s (was 15-30s) +5. **Total**: ~1.2-4.5s per question + +--- + +## ๐Ÿ”ง Technical Changes + +### Files Modified: + +1. **`tests/load_tests/test_generic_load_assessments.py`** + - Removed `RandomizedWait.wait_for_question_answer()` + - Added minimal 0.1s wait for click registration + +2. **`utils/wait_helpers.py`** + - `wait_for_page_load()`: Smart check, returns immediately if ready + - `wait_for_loading_to_disappear()`: Reduced timeout 2s โ†’ 0.5s + +3. **`utils/question_answer_helper.py`** + - All `_element_exists()` timeouts: 2-3s โ†’ 0.5s + - All `WebDriverWait` timeouts: 10s โ†’ 2s + - Smart wait for question container (0.5s instead of hardcoded sleep) + +4. **`pages/domain_assessment_page.py`** + - Optimized `click_next()` with smart waits + +5. **`pages/base_page.py`** + - Optimized `wait_for_page_load()` with smart waits + +--- + +## โšก Key Optimizations + +### 1. **Early Return Pattern** +```python +# Before: Always waits full timeout +WebDriverWait(driver, 15).until(condition) + +# After: Returns immediately if ready +if condition_met(): + return +WebDriverWait(driver, 3).until(condition) +``` + +### 2. **Minimal Timeouts** +- Question detection: 0.5s (was 2-3s) +- Element waits: 2s (was 10s) +- Loading indicator: 0.5s (was 2s) +- Page load: 3s with early return (was 15s) + +### 3. **Removed Human-Like Delays** +- No "thinking time" after answering +- No "reading time" for options +- No "navigation delay" after clicking +- Minimal wait only for click registration (0.1s) + +--- + +## ๐ŸŽฏ Expected Results + +### Single Student (100 Questions): +- **Before**: ~42 minutes +- **After**: ~5-13 minutes +- **Improvement**: **70-80% faster** + +### Load Test (100 Students): +- **Before**: ~42 minutes per student (sequential) +- **After**: ~5-13 minutes per student (sequential) +- **With 30 workers**: ~20-40 minutes total (was 2+ hours) + +--- + +## โš ๏ธ Safety Considerations + +1. **Explicit Waits Preserved**: Still using explicit waits for reliability +2. **Smart Waits**: Return immediately when ready, don't wait unnecessarily +3. **Minimum Timeouts**: Reduced but not eliminated (still safe) +4. **No Logic Changes**: Only wait-time optimizations, no functional changes + +--- + +## ๐Ÿงช Testing Recommendations + +### Phase 1: Single Student Test +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless +``` + +**Expected**: Should complete in ~5-13 minutes (was 42 minutes) + +### Phase 2: Small Batch Test +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 10 --workers 5 --headless +``` + +**Expected**: Should complete in ~10-20 minutes (was 42 minutes per student) + +### Phase 3: Full Load Test +```bash +./scripts/PC1_100_students.sh +``` + +**Expected**: Should complete in ~20-40 minutes (was 2+ hours) + +--- + +## ๐Ÿ“ˆ Success Metrics + +- **Target**: <8 seconds per question (average) +- **Target**: <15 minutes for 100 questions +- **Target**: 0% increase in failure rate +- **Target**: Maintain 100% reliability +- **Target**: Machine-speed execution (no human-like delays) + +--- + +## ๐Ÿ’ก Philosophy + +**Before**: "Simulate human behavior with realistic delays" +**After**: "Machine-speed execution with minimal necessary waits" + +The automation now: +- โœ… Returns immediately when conditions are met +- โœ… Uses minimal timeouts (not excessive) +- โœ… Removes all "thinking time" delays +- โœ… Optimizes for speed while maintaining reliability + +--- + +**Status**: โœ… Ready for Testing - Machine-Speed Optimized + diff --git a/tests/load_tests/OPTIMIZATION_SUMMARY.md b/tests/load_tests/OPTIMIZATION_SUMMARY.md new file mode 100644 index 0000000..8d00ff2 --- /dev/null +++ b/tests/load_tests/OPTIMIZATION_SUMMARY.md @@ -0,0 +1,131 @@ +# Performance Optimization Summary + +## โœ… Optimizations Applied + +### 1. **Removed Redundant Waits** (Major Impact) +- **Removed**: `RandomizedWait.wait_for_navigation('next')` after all `click_next()` calls +- **Reason**: `click_next()` already waits for page load internally +- **Savings**: ~1-3 seconds per question +- **Locations**: 4 places in `test_generic_load_assessments.py` + +### 2. **Removed Unnecessary Start Wait** (Medium Impact) +- **Removed**: `RandomizedWait.wait_for_page_load('navigation')` at start of question loop +- **Reason**: Page is already loaded from previous Next click +- **Savings**: ~1-3 seconds per question +- **Location**: Start of question loop in `test_generic_load_assessments.py` + +### 3. **Optimized Loading Indicator Wait** (Major Impact) +- **Changed**: `wait_for_loading_to_disappear()` timeout from 15s to 2s +- **Reason**: Loading indicators disappear quickly (1-2s), and if they don't exist, we shouldn't wait +- **Savings**: ~10-13 seconds per question (when loading doesn't exist) +- **Location**: `utils/wait_helpers.py` + +### 4. **Replaced Hardcoded Sleep** (Small Impact) +- **Changed**: `time.sleep(0.5)` โ†’ Smart wait for question element visibility +- **Reason**: Waits only if element not ready, returns immediately if ready +- **Savings**: ~0.3-0.5 seconds per question +- **Location**: `utils/question_answer_helper.py` + +--- + +## ๐Ÿ“Š Expected Performance Improvement + +### Before Optimization: +- **Per Question**: ~25 seconds (observed) +- **100 Questions**: ~42 minutes (observed) + +### After Optimization: +- **Per Question**: ~10-15 seconds (estimated) +- **100 Questions**: ~17-25 minutes (estimated) +- **Improvement**: **~40-60% faster** + +### Breakdown (Optimized): +1. Get question type: 0.1-0.5s (was 0.5s) +2. Answer question: 2-6s (unchanged) +3. After answer wait: 2-6s (unchanged) +4. Click Next + page load: 1-3s (was 15-30s) +5. **Total**: ~5-15s per question + +--- + +## ๐Ÿงช Testing Recommendations + +### Phase 1: Single Student Test +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 1 --workers 1 --headless +``` + +**Expected**: Should complete in ~10-15 minutes (was 50+ minutes) + +### Phase 2: Small Batch Test +```bash +python3 tests/load_tests/test_generic_load_assessments.py \ + --csv students_with_passwords_2025-12-15T10-49-08_01.csv \ + --start 0 --end 10 --workers 5 --headless +``` + +**Expected**: Should complete in ~15-20 minutes (was 50+ minutes per student) + +### Phase 3: Full Load Test +```bash +./scripts/PC1_100_students.sh +``` + +**Expected**: Should complete in ~20-30 minutes (was 50+ minutes per student) + +--- + +## โš ๏ธ Safety Notes + +1. **No Functional Changes**: All optimizations are wait-time reductions, no logic changes +2. **Explicit Waits Preserved**: Still using explicit waits for reliability +3. **Smart Waits**: Replaced fixed sleeps with smart waits that return immediately when ready +4. **Backward Compatible**: All changes are safe and won't break existing flows + +--- + +## ๐Ÿ” What to Monitor + +1. **Failure Rate**: Should remain 0% (same as before) +2. **Question Detection**: Should still detect all questions correctly +3. **Navigation**: Should still navigate between questions smoothly +4. **Answer Submission**: Should still submit answers correctly + +--- + +## ๐Ÿ“ Files Modified + +1. `tests/load_tests/test_generic_load_assessments.py` + - Removed 4 redundant `RandomizedWait.wait_for_navigation()` calls + - Removed 1 redundant `RandomizedWait.wait_for_page_load()` call + +2. `utils/wait_helpers.py` + - Optimized `wait_for_loading_to_disappear()` timeout from 15s to 2s + +3. `utils/question_answer_helper.py` + - Replaced `time.sleep(0.5)` with smart wait for question element + +--- + +## ๐ŸŽฏ Next Steps + +1. โœ… Run single student test to verify no breakage +2. โœ… Monitor performance improvement +3. โœ… If successful, proceed with full load test +4. โœ… Document actual performance gains + +--- + +## ๐Ÿ’ก Additional Optimization Opportunities (Future) + +1. **Parallel Question Detection**: Detect question type while waiting for answer +2. **Batch Answer Submission**: Submit multiple answers at once (if backend supports) +3. **Connection Pooling**: Reuse connections for faster API calls +4. **Smart Retry Logic**: Retry only on actual failures, not timeouts + +--- + +**Status**: โœ… Ready for Testing + diff --git a/tests/load_tests/PERFORMANCE_OPTIMIZATION_ANALYSIS.md b/tests/load_tests/PERFORMANCE_OPTIMIZATION_ANALYSIS.md new file mode 100644 index 0000000..edfd691 --- /dev/null +++ b/tests/load_tests/PERFORMANCE_OPTIMIZATION_ANALYSIS.md @@ -0,0 +1,133 @@ +# Performance Optimization Analysis - Question Timing + +## ๐Ÿ” Current Problem + +**Observed**: ~25 seconds per question (50+ minutes for 100 questions) + +**Expected**: ~4-8 seconds per question (7-13 minutes for 100 questions) + +--- + +## ๐Ÿ“Š Current Wait Chain Per Question + +### Breakdown of Current Waits: + +1. **Start of loop**: `RandomizedWait.wait_for_page_load('navigation')` โ†’ **1-3s** +2. **Get question type**: `time.sleep(0.5)` โ†’ **0.5s** (hardcoded!) +3. **Answer question**: Depends on type โ†’ **2-6s** (multiple choice) +4. **After answer**: `RandomizedWait.wait_for_question_answer()` โ†’ **2-6s** +5. **Click Next**: `click_next()` which calls: + - `wait_for_page_load()` โ†’ **Up to 15s** (waits for document.readyState) + - `wait_for_loading_to_disappear()` โ†’ **Up to 15s** (waits for loading indicator) +6. **After Next**: `RandomizedWait.wait_for_navigation('next')` โ†’ **1-3s** (REDUNDANT!) + +### Total Per Question: +- **Minimum**: 1 + 0.5 + 2 + 2 + 0 + 0 + 1 = **6.5s** +- **Maximum**: 3 + 0.5 + 6 + 6 + 15 + 15 + 3 = **48.5s** +- **Average**: ~2 + 0.5 + 4 + 4 + 5 + 5 + 2 = **22.5s** โœ… **Matches your observation!** + +--- + +## ๐Ÿ› Issues Identified + +### 1. **Redundant Waits** (Major Issue) +- `click_next()` already waits for page load +- Then we wait again with `RandomizedWait.wait_for_navigation()` +- **Waste**: 1-3 seconds per question + +### 2. **Hardcoded Sleep** (Minor Issue) +- `get_question_type()` has `time.sleep(0.5)` +- Should use smart wait instead +- **Waste**: 0.5 seconds per question + +### 3. **Excessive Explicit Waits** (Major Issue) +- `wait_for_loading_to_disappear()` waits up to 15 seconds +- Loading indicators usually disappear in 1-2 seconds +- **Waste**: 10-13 seconds per question (if loading doesn't exist) + +### 4. **Double Page Load Check** (Minor Issue) +- `wait_for_page_load()` checks document.readyState +- `wait_for_loading_to_disappear()` checks loading indicator +- Both might be redundant +- **Waste**: 5-10 seconds per question + +### 5. **Unnecessary Start Wait** (Minor Issue) +- `RandomizedWait.wait_for_page_load('navigation')` at start of loop +- Page is already loaded from previous Next click +- **Waste**: 1-3 seconds per question + +--- + +## โœ… Optimization Strategy + +### Phase 1: Remove Redundant Waits (Quick Win) +1. Remove `RandomizedWait.wait_for_navigation('next')` after `click_next()` +2. Remove `RandomizedWait.wait_for_page_load('navigation')` at start of loop +3. **Savings**: ~2-6 seconds per question + +### Phase 2: Optimize Explicit Waits (Medium Win) +1. Reduce `wait_for_loading_to_disappear()` timeout to 2-3 seconds +2. Make it non-blocking (don't fail if loading doesn't exist) +3. **Savings**: ~10-13 seconds per question (if loading doesn't exist) + +### Phase 3: Replace Hardcoded Sleeps (Small Win) +1. Replace `time.sleep(0.5)` in `get_question_type()` with smart wait +2. Wait for question element to be visible instead +3. **Savings**: ~0.3-0.5 seconds per question + +### Phase 4: Smart Wait Strategy (Advanced) +1. Use explicit waits that return immediately when condition is met +2. Don't wait full timeout if element is ready +3. **Savings**: Variable, but significant + +--- + +## ๐ŸŽฏ Expected Results After Optimization + +### Optimized Wait Chain: + +1. **Get question type**: Smart wait โ†’ **0.1-0.5s** (was 0.5s) +2. **Answer question**: Depends on type โ†’ **2-6s** (unchanged) +3. **After answer**: `RandomizedWait.wait_for_question_answer()` โ†’ **2-6s** (unchanged) +4. **Click Next**: `click_next()` with optimized waits โ†’ **1-3s** (was 15-30s) +5. **No redundant wait** โ†’ **0s** (was 1-3s) + +### Total Per Question (Optimized): +- **Minimum**: 0.1 + 2 + 2 + 1 = **5.1s** +- **Maximum**: 0.5 + 6 + 6 + 3 = **15.5s** +- **Average**: ~0.3 + 4 + 4 + 2 = **10.3s** + +### For 100 Questions: +- **Current**: ~25s ร— 100 = **~42 minutes** +- **Optimized**: ~10s ร— 100 = **~17 minutes** +- **Improvement**: **~60% faster** (25 minutes saved!) + +--- + +## ๐Ÿš€ Implementation Plan + +1. โœ… Remove redundant `RandomizedWait.wait_for_navigation()` calls +2. โœ… Remove redundant `RandomizedWait.wait_for_page_load()` at start +3. โœ… Optimize `wait_for_loading_to_disappear()` timeout (2-3s instead of 15s) +4. โœ… Replace hardcoded `time.sleep(0.5)` with smart wait +5. โœ… Test with 1 student to verify no breakage +6. โœ… Test with 10 students to verify performance improvement + +--- + +## โš ๏ธ Safety Considerations + +- **Don't remove explicit waits entirely** - they ensure elements are ready +- **Keep minimum wait times** - prevents race conditions +- **Test thoroughly** - ensure no flakiness introduced +- **Monitor for failures** - if optimization causes issues, revert + +--- + +## ๐Ÿ“ˆ Success Metrics + +- **Target**: <15 seconds per question (average) +- **Target**: <20 minutes for 100 questions +- **Target**: 0% increase in failure rate +- **Target**: Maintain 100% reliability + diff --git a/tests/load_tests/QUESTION_TIMING_ANALYSIS.md b/tests/load_tests/QUESTION_TIMING_ANALYSIS.md new file mode 100644 index 0000000..3732455 --- /dev/null +++ b/tests/load_tests/QUESTION_TIMING_ANALYSIS.md @@ -0,0 +1,131 @@ +# Question Timing Analysis + +## โฑ๏ธ Current Per-Question Timing (Approximate) + +Based on `RandomizedWait` wait ranges, here's how long each question type takes: + +### Question Answer Times + +| Question Type | Min Time | Max Time | Average | Notes | +|--------------|----------|----------|---------|-------| +| **Rating Scale** | 1s | 4s | ~2.5s | Quick selection (1-5 stars) | +| **Multiple Choice** | 2s | 6s | ~4s | Reading options (A, B, C, D, E) | +| **True/False** | 1s | 3s | ~2s | Binary choice (True/False) | +| **Open Ended** | 5s | 15s | ~10s | Typing text response | +| **Matrix** | 3s | 8s | ~5.5s | Multiple cell selections | + +### Navigation Times + +| Action | Min Time | Max Time | Average | +|--------|----------|----------|---------| +| **Click Next** | 1s | 3s | ~2s | +| **Page Load** | 1s | 3s | ~2s | + +### Total Time Per Question (Approximate) + +**Formula**: Answer Time + Navigation Time + +| Question Type | Min Total | Max Total | Average Total | +|--------------|-----------|-----------|---------------| +| Rating Scale | 2s | 7s | ~4.5s | +| Multiple Choice | 3s | 9s | ~6s | +| True/False | 2s | 6s | ~4s | +| Open Ended | 6s | 18s | ~12s | +| Matrix | 4s | 11s | ~7.5s | + +### Example: 108 Questions (Emotional Intelligence) + +If all questions are **Multiple Choice** (most common): +- **Average**: 108 questions ร— 6s = **~10.8 minutes** +- **Min**: 108 questions ร— 3s = **~5.4 minutes** +- **Max**: 108 questions ร— 9s = **~16.2 minutes** + +If mixed question types: +- **Average**: ~8-12 minutes (depending on mix) +- **Min**: ~5-7 minutes +- **Max**: ~15-20 minutes + +--- + +## ๐Ÿ“Š What We Currently Track + +### In Load Test Results + +โœ… **Tracked:** +- Total duration per student (entire flow) +- Total questions answered +- Average duration across students +- Success/failure rate + +โŒ **NOT Tracked:** +- Time per individual question +- Time breakdown by question type +- Question-level performance metrics + +--- + +## ๐Ÿ”ง Adding Per-Question Timing (Optional) + +If you want to track per-question timing, I can add: + +1. **Question-level timing** in load test results +2. **Breakdown by question type** (avg time per type) +3. **Detailed metrics** in JSON report + +**Would you like me to add this?** + +--- + +## ๐Ÿ’ก Current Behavior + +### How Questions Are Answered + +```python +# For each question: +1. Detect question (0.5-1s) +2. Answer question (1-15s depending on type) +3. Wait for answer confirmation (0.5-1s) +4. Click Next (1-3s) +5. Wait for next question to load (1-3s) + +Total: ~4-20s per question (depending on type) +``` + +### Realistic Timing + +The automation uses **randomized waits** to simulate human behavior: +- Not too fast (doesn't look like a bot) +- Not too slow (efficient testing) +- Varies per question type (realistic) + +--- + +## ๐Ÿ“ˆ Backend Perspective + +The **backend** tracks time per question when answers are submitted: +- Frontend sends `timeSpent` with each answer +- Backend stores it in the database +- Available in backend analytics + +**But our automation doesn't currently track this** - we just use realistic wait times. + +--- + +## ๐ŸŽฏ Summary + +**Current State:** +- โœ… We know approximate times per question type +- โœ… We track total duration per student +- โŒ We don't track individual question times + +**Approximate Times:** +- **Fast questions** (True/False, Rating): ~2-4s each +- **Medium questions** (Multiple Choice, Matrix): ~4-8s each +- **Slow questions** (Open Ended): ~6-18s each + +**For 108 questions:** +- **Average**: ~8-12 minutes +- **Range**: ~5-20 minutes (depending on question types) + +Would you like me to add detailed per-question timing tracking to the load test? + diff --git a/tests/load_tests/test_generic_load_assessments.py b/tests/load_tests/test_generic_load_assessments.py index 8995f06..28e21e9 100644 --- a/tests/load_tests/test_generic_load_assessments.py +++ b/tests/load_tests/test_generic_load_assessments.py @@ -404,7 +404,7 @@ def complete_assessment_flow_for_student( max_consecutive_failures = 3 while questions_answered < max_questions: - RandomizedWait.wait_for_page_load('navigation') + # Removed redundant wait_for_page_load - page is already loaded from previous Next click # Get current question ID question_id = question_helper.get_question_id() @@ -418,7 +418,7 @@ def complete_assessment_flow_for_student( if domain_assessment_page.is_next_button_visible(): try: domain_assessment_page.click_next() - RandomizedWait.wait_for_navigation('next') + # Removed redundant wait_for_navigation - click_next() already waits except: pass continue @@ -433,7 +433,8 @@ def complete_assessment_flow_for_student( f"[data-testid='domain_question__{question_id}']" ) driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", question_elem) - RandomizedWait.wait_for_page_load('navigation') + # Small wait for scroll animation, but not full page load + RandomizedWait.wait_for_page_load('modal') # Use shorter modal wait instead question_type = question_helper.get_question_type(question_id) except: pass @@ -441,7 +442,7 @@ def complete_assessment_flow_for_student( if question_type == "unknown": if domain_assessment_page.is_next_button_visible(): domain_assessment_page.click_next() - RandomizedWait.wait_for_navigation('next') + # Removed redundant wait_for_navigation - click_next() already waits continue # Answer the question @@ -449,7 +450,8 @@ def complete_assessment_flow_for_student( question_helper.answer_question(question_id, question_type) questions_answered += 1 consecutive_failures = 0 - RandomizedWait.wait_for_question_answer(question_type) + # Machine-speed: Minimal wait for click to register (0.1s instead of 2-6s) + time.sleep(0.1) except Exception as e: consecutive_failures += 1 if consecutive_failures >= max_consecutive_failures: @@ -457,7 +459,7 @@ def complete_assessment_flow_for_student( if domain_assessment_page.is_next_button_visible(): try: domain_assessment_page.click_next() - RandomizedWait.wait_for_navigation('next') + # Removed redundant wait_for_navigation - click_next() already waits except: pass continue @@ -481,7 +483,7 @@ def complete_assessment_flow_for_student( if domain_assessment_page.is_next_button_visible(): try: domain_assessment_page.click_next() - RandomizedWait.wait_for_navigation('next') + # Removed redundant wait_for_navigation - click_next() already waits except Exception as e: print(f"โš ๏ธ Error clicking Next after question {question_id}: {e}") # Try to continue anyway @@ -761,9 +763,28 @@ if __name__ == "__main__": parser.add_argument('--headless', action='store_true', default=True, help='Run in headless mode') parser.add_argument('--visible', action='store_true', help='Run in visible mode (overrides headless)') parser.add_argument('--metrics-interval', type=int, default=10, help='Print metrics every N students') + parser.add_argument('--url', type=str, default=None, help='Frontend URL to use (e.g., http://localhost:3983 or https://cognitiveprism.tech4bizsolutions.com). If not provided, uses default from config.') args = parser.parse_args() + # Override BASE_URL if --url is provided + if args.url: + # Remove trailing slash if present + custom_url = args.url.rstrip('/') + # Override BASE_URL in config before importing pages + import config.config as config_module + config_module.BASE_URL = custom_url + # Update all derived URLs + config_module.LOGIN_URL = f"{custom_url}/" + config_module.DASHBOARD_URL = f"{custom_url}/student/dashboard" + config_module.ASSESSMENTS_URL = f"{custom_url}/assessments" + config_module.PROFILE_EDITOR_URL = f"{custom_url}/student/profile-builder" + print(f"๐ŸŒ Using custom URL: {custom_url}") + else: + # Import to show default URL + from config.config import BASE_URL + print(f"๐ŸŒ Using default URL: {BASE_URL}") + run_assessment_load_test( csv_path=args.csv, start_index=args.start, diff --git a/utils/question_answer_helper.py b/utils/question_answer_helper.py index 4fde19e..27fe4ad 100644 --- a/utils/question_answer_helper.py +++ b/utils/question_answer_helper.py @@ -84,54 +84,64 @@ class QuestionAnswerHelper: str: Question type (multiple_choice, true_false, rating_scale, open_ended, matrix) """ try: - # Wait a moment for question to fully render - import time - time.sleep(0.5) + # Machine-speed: Quick check if question is already visible (returns immediately if ready) + try: + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + WebDriverWait(self.driver, 0.5).until( + EC.presence_of_element_located( + (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}']") + ) + ) + except: + # Question might already be visible, continue + pass # First, check for container elements (more reliable) - # Check for multiple choice container - if self._element_exists(f"[data-testid='domain_question__{question_id}__multiple_choice']", timeout=3): + # Machine-speed: Reduced timeout from 3s to 0.5s + if self._element_exists(f"[data-testid='domain_question__{question_id}__multiple_choice']", timeout=0.5): return "multiple_choice" - # Check for true/false container - if self._element_exists(f"[data-testid='domain_question__{question_id}__true_false']", timeout=3): + # Check for true/false container - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__true_false']", timeout=0.5): return "true_false" - # Check for rating scale container - if self._element_exists(f"[data-testid='domain_question__{question_id}__rating_scale']", timeout=3): + # Check for rating scale container - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__rating_scale']", timeout=0.5): return "rating_scale" - # Check for open ended container - if self._element_exists(f"[data-testid='domain_question__{question_id}__open_ended']", timeout=3): + # Check for open ended container - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__open_ended']", timeout=0.5): return "open_ended" - # Check for matrix container - if self._element_exists(f"[data-testid='domain_question__{question_id}__matrix']", timeout=3): + # Check for matrix container - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__matrix']", timeout=0.5): return "matrix" # Fallback: Check for individual answer elements (if containers not found) - # Check for multiple choice options (A, B, C, D, E) + # Check for multiple choice options (A, B, C, D, E) - machine-speed: 0.5s timeout for label in ['A', 'B', 'C', 'D', 'E']: - if self._element_exists(f"[data-testid='domain_question__{question_id}__option_{label}']", timeout=2): + if self._element_exists(f"[data-testid='domain_question__{question_id}__option_{label}']", timeout=0.5): return "multiple_choice" - # Check for true/false buttons - if self._element_exists(f"[data-testid='domain_question__{question_id}__truefalse_True']", timeout=2): + # Check for true/false buttons - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__truefalse_True']", timeout=0.5): return "true_false" - if self._element_exists(f"[data-testid='domain_question__{question_id}__truefalse_False']", timeout=2): + if self._element_exists(f"[data-testid='domain_question__{question_id}__truefalse_False']", timeout=0.5): return "true_false" - # Check for rating scale buttons + # Check for rating scale buttons - machine-speed: 0.5s timeout for rating in ['1', '2', '3', '4', '5']: - if self._element_exists(f"[data-testid='domain_question__{question_id}__rating_{rating}']", timeout=2): + if self._element_exists(f"[data-testid='domain_question__{question_id}__rating_{rating}']", timeout=0.5): return "rating_scale" - # Check for open ended textarea - if self._element_exists(f"[data-testid='domain_question__{question_id}__textarea']", timeout=2): + # Check for open ended textarea - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid='domain_question__{question_id}__textarea']", timeout=0.5): return "open_ended" - # Check for matrix cells - if self._element_exists(f"[data-testid^='domain_question__{question_id}__matrix_']", timeout=2): + # Check for matrix cells - machine-speed: 0.5s timeout + if self._element_exists(f"[data-testid^='domain_question__{question_id}__matrix_']", timeout=0.5): return "matrix" return "unknown" @@ -139,8 +149,8 @@ class QuestionAnswerHelper: print(f"โš ๏ธ Error detecting question type for {question_id}: {e}") return "unknown" - def _element_exists(self, css_selector, timeout=2): - """Check if element exists quickly""" + def _element_exists(self, css_selector, timeout=0.5): + """Check if element exists quickly (machine-speed optimized)""" try: WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located((By.CSS_SELECTOR, css_selector)) @@ -160,12 +170,12 @@ class QuestionAnswerHelper: Returns: str: Selected option label """ - # Get available options + # Get available options (machine-speed: reduced timeout from 2s to 0.5s) options = [] for label in ['A', 'B', 'C', 'D', 'E']: locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__option_{label}']") try: - element = WebDriverWait(self.driver, 2).until( + element = WebDriverWait(self.driver, 0.5).until( EC.presence_of_element_located(locator) ) if element.is_displayed(): @@ -183,7 +193,8 @@ class QuestionAnswerHelper: option_label = random.choice(options) # Fallback to random locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__option_{option_label}']") - element = WebDriverWait(self.driver, self.wait_timeout).until( + # Machine-speed: Reduced timeout from 10s to 2s (element should be ready quickly) + element = WebDriverWait(self.driver, 2).until( EC.element_to_be_clickable(locator) ) element.click() @@ -207,7 +218,8 @@ class QuestionAnswerHelper: value_str = "True" if value else "False" locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__truefalse_{value_str}']") - element = WebDriverWait(self.driver, self.wait_timeout).until( + # Machine-speed: Reduced timeout from 10s to 2s + element = WebDriverWait(self.driver, 2).until( EC.element_to_be_clickable(locator) ) element.click() @@ -276,7 +288,8 @@ class QuestionAnswerHelper: locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__rating_{selected_value}']") - element = WebDriverWait(self.driver, self.wait_timeout).until( + # Machine-speed: Reduced timeout from 10s to 2s + element = WebDriverWait(self.driver, 2).until( EC.element_to_be_clickable(locator) ) element.click() @@ -299,7 +312,8 @@ class QuestionAnswerHelper: locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__textarea']") - element = WebDriverWait(self.driver, self.wait_timeout).until( + # Machine-speed: Reduced timeout from 10s to 2s + element = WebDriverWait(self.driver, 2).until( EC.presence_of_element_located(locator) ) element.clear() @@ -357,7 +371,8 @@ class QuestionAnswerHelper: locator = (By.CSS_SELECTOR, f"[data-testid='domain_question__{question_id}__matrix_{row_index}_{column_index}']") - element = WebDriverWait(self.driver, self.wait_timeout).until( + # Machine-speed: Reduced timeout from 10s to 2s + element = WebDriverWait(self.driver, 2).until( EC.element_to_be_clickable(locator) ) element.click() diff --git a/utils/wait_helpers.py b/utils/wait_helpers.py index dd74370..055bb0d 100644 --- a/utils/wait_helpers.py +++ b/utils/wait_helpers.py @@ -105,24 +105,35 @@ class WaitHelpers: return self.wait.until(EC.url_contains(url_part)) def wait_for_page_load(self): - """Wait for page to load completely""" - self.wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete") + """Wait for page to load completely (machine-speed optimized)""" + # Check if already loaded - return immediately if ready + if self.driver.execute_script("return document.readyState") == "complete": + return + # Otherwise wait, but with shorter timeout for machine-speed + try: + WebDriverWait(self.driver, 3).until( + lambda driver: driver.execute_script("return document.readyState") == "complete" + ) + except TimeoutException: + # Page might be interactive even if not "complete" - continue + pass def wait_for_loading_to_disappear(self, timeout=None): """ - Wait for loading indicator to disappear + Wait for loading indicator to disappear (machine-speed optimized) Args: - timeout: Optional timeout override + timeout: Optional timeout override (defaults to 0.5s for machine-speed) """ - wait_time = timeout or EXPLICIT_WAIT + # Machine-speed: Use minimal timeout (0.5s) - loading indicators disappear instantly or don't exist + wait_time = timeout or 0.5 # Reduced from 2s to 0.5s for machine-speed wait = WebDriverWait(self.driver, wait_time) try: wait.until(EC.invisibility_of_element_located( (By.XPATH, "//div[contains(., 'Loading')]") )) except TimeoutException: - # Loading might not be present, which is fine + # Loading might not be present, which is fine - don't fail pass