diff --git a/capture_log.txt b/capture_log.txt new file mode 100644 index 000000000..3df83e122 --- /dev/null +++ b/capture_log.txt @@ -0,0 +1 @@ +bash: ./capture_all_tasks_complete.sh: No such file or directory diff --git a/cleanup_screenshots_ocr.py b/cleanup_screenshots_ocr.py new file mode 100644 index 000000000..f14711ff5 --- /dev/null +++ b/cleanup_screenshots_ocr.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 + +import os +import sys +import shutil +import json +from datetime import datetime +from collections import defaultdict +import cv2 +import numpy as np + +try: + import pytesseract + from PIL import Image + from difflib import SequenceMatcher +except ImportError as e: + print(f"Error: Missing required dependency: {e}") + print("Please install required packages:") + print("pip install pytesseract pillow opencv-python") + sys.exit(1) + +def extract_text_from_image(image_path): + """Extract text from image using OCR, focusing on top half""" + try: + # Open image and crop to top half for better text extraction + with Image.open(image_path) as img: + width, height = img.size + top_half = img.crop((0, 0, width, height // 2)) + + # Extract text using OCR + text = pytesseract.image_to_string(top_half, config='--psm 6') + return text.strip().lower() + except Exception as e: + print(f"Error extracting text from {image_path}: {e}") + return "" + +def calculate_image_similarity(img1_path, img2_path): + """Calculate image similarity using correlation coefficient""" + try: + # Read images in grayscale + img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE) + img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE) + + if img1 is None or img2 is None: + return 0.0 + + # Resize images to same size for comparison + height, width = min(img1.shape[0], img2.shape[0]), min(img1.shape[1], img2.shape[1]) + img1_resized = cv2.resize(img1, (width, height)) + img2_resized = cv2.resize(img2, (width, height)) + + # Calculate correlation coefficient + correlation = cv2.matchTemplate(img1_resized, img2_resized, cv2.TM_CCOEFF_NORMED)[0][0] + return max(0.0, correlation) # Ensure non-negative + + except Exception as e: + print(f"Error calculating image similarity between {img1_path} and {img2_path}: {e}") + return 0.0 + +def text_similarity(text1, text2): + """Calculate text similarity using sequence matcher""" + if not text1 and not text2: + return 1.0 + if not text1 or not text2: + return 0.0 + return SequenceMatcher(None, text1, text2).ratio() + +def are_duplicates(file1, file2, text1, text2): + """ + Enhanced duplicate detection using both text and image similarity + Returns True if items are duplicates (both text AND image highly similar) + """ + # Calculate text similarity + text_sim = text_similarity(text1, text2) + + # Calculate image similarity + image_sim = calculate_image_similarity(file1, file2) + + # Consider duplicates only if BOTH text and image are highly similar + text_threshold = 0.80 + image_threshold = 0.95 + + is_duplicate = (text_sim >= text_threshold) and (image_sim >= image_threshold) + + print(f" Comparing {os.path.basename(file1)} vs {os.path.basename(file2)}") + print(f" Text similarity: {text_sim:.3f} (threshold: {text_threshold})") + print(f" Image similarity: {image_sim:.3f} (threshold: {image_threshold})") + print(f" Duplicate: {is_duplicate}") + + return is_duplicate + +def group_duplicates(screenshot_files): + """Group screenshots by similarity using enhanced detection""" + print("Extracting text from screenshots...") + + # Extract text from all images + image_texts = {} + for file_path in screenshot_files: + text = extract_text_from_image(file_path) + image_texts[file_path] = text + print(f" {os.path.basename(file_path)}: '{text[:50]}{'...' if len(text) > 50 else ''}'") + + print("\nGrouping duplicates using enhanced detection...") + + groups = [] + processed = set() + + for i, file1 in enumerate(screenshot_files): + if file1 in processed: + continue + + # Start a new group with this file + current_group = [file1] + processed.add(file1) + + # Find all duplicates of this file + for j, file2 in enumerate(screenshot_files[i+1:], i+1): + if file2 in processed: + continue + + if are_duplicates(file1, file2, image_texts[file1], image_texts[file2]): + current_group.append(file2) + processed.add(file2) + + groups.append(current_group) + print(f"Group {len(groups)}: {len(current_group)} files") + + return groups + +def cleanup_screenshots(directory): + """Main cleanup function with enhanced duplicate detection""" + if not os.path.exists(directory): + print(f"Error: Directory {directory} does not exist") + return + + # Find all PNG files + screenshot_files = [] + for file in os.listdir(directory): + if file.lower().endswith('.png'): + screenshot_files.append(os.path.join(directory, file)) + + if not screenshot_files: + print(f"No PNG files found in {directory}") + return + + screenshot_files.sort() + total_files = len(screenshot_files) + + print(f"Found {total_files} screenshot files in {directory}") + print("=" * 60) + + # Group duplicates using enhanced detection + groups = group_duplicates(screenshot_files) + + # Create duplicates backup directory + duplicates_dir = os.path.join(directory, "duplicates_backup") + os.makedirs(duplicates_dir, exist_ok=True) + + # Process groups + kept_files = [] + moved_files = [] + + for i, group in enumerate(groups, 1): + if len(group) == 1: + # Single file, keep it + kept_files.extend(group) + print(f"\nGroup {i}: Keeping unique file {os.path.basename(group[0])}") + else: + # Multiple files, keep first and move others + keep_file = group[0] + duplicate_files = group[1:] + + kept_files.append(keep_file) + moved_files.extend(duplicate_files) + + print(f"\nGroup {i}: Keeping {os.path.basename(keep_file)}, moving {len(duplicate_files)} duplicates") + + # Move duplicates to backup directory + for duplicate in duplicate_files: + backup_path = os.path.join(duplicates_dir, os.path.basename(duplicate)) + # Handle name conflicts + counter = 1 + while os.path.exists(backup_path): + name, ext = os.path.splitext(os.path.basename(duplicate)) + backup_path = os.path.join(duplicates_dir, f"{name}_{counter}{ext}") + counter += 1 + + shutil.move(duplicate, backup_path) + print(f" Moved {os.path.basename(duplicate)} to duplicates_backup/") + + # Generate statistics + unique_files = len(kept_files) + duplicates_removed = len(moved_files) + reduction_percentage = (duplicates_removed / total_files) * 100 if total_files > 0 else 0 + + print("\n" + "=" * 60) + print("CLEANUP SUMMARY") + print("=" * 60) + print(f"Total files processed: {total_files}") + print(f"Unique files kept: {unique_files}") + print(f"Duplicates moved to backup: {duplicates_removed}") + print(f"Reduction: {reduction_percentage:.1f}%") + print(f"Duplicates backed up to: {duplicates_dir}") + + # Save analysis report + report = { + "timestamp": datetime.now().isoformat(), + "directory": directory, + "total_files": total_files, + "unique_files": unique_files, + "duplicates_removed": duplicates_removed, + "reduction_percentage": reduction_percentage, + "groups": len(groups), + "duplicates_backup_dir": duplicates_dir + } + + report_file = os.path.join(directory, "ocr_analysis_report.json") + with open(report_file, 'w') as f: + json.dump(report, f, indent=2) + + print(f"Analysis report saved to: {report_file}") + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python cleanup_screenshots_ocr.py ") + print("Example: python cleanup_screenshots_ocr.py cypress/screenshots/memory_game_capture.cy.js/") + sys.exit(1) + + directory = sys.argv[1] + cleanup_screenshots(directory) \ No newline at end of file diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 000000000..a4a1edf3b --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,31 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + // Video recording settings + video: true, + videoCompression: 32, + videosFolder: 'cypress/videos', + screenshotsFolder: 'cypress/screenshots', + viewportWidth: 1000, + viewportHeight: 660, + defaultCommandTimeout: 30000, + requestTimeout: 30000, + responseTimeout: 30000, + pageLoadTimeout: 60000, + // Memory management settings + experimentalMemoryManagement: true, + numTestsKeptInMemory: 1, + // Reduce video frame rate for smaller files + env: { + videoFrameRate: 5 // Lower frame rate for more compact videos + } + }, + retries: { + runMode: 0, + openMode: 0, + }, +}); diff --git a/cypress/e2e/adult_reasoning_improved.cy.js b/cypress/e2e/adult_reasoning_improved.cy.js new file mode 100644 index 000000000..5475fa6d4 --- /dev/null +++ b/cypress/e2e/adult_reasoning_improved.cy.js @@ -0,0 +1,72 @@ +describe('Adult Reasoning Improved Screenshot Capture', () => { + it('captures screenshots from adult-reasoning with proper interactions', () => { + cy.visit('http://localhost:8080/?task=adult-reasoning'); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Initial screenshot + cy.screenshot('01-initial-load'); + + // Function to handle interactions and take screenshots + const captureWithInteraction = (screenshotName) => { + cy.wait(8000); // Wait 8 seconds between screenshots + + // Try to interact with the task + cy.get('body').then(($body) => { + // Check for fullscreen/start buttons first + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Check for Continue buttons + else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Check for multi-response buttons (main task responses) + else if ($body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + // Click a random response button + cy.get('#jspsych-html-multi-response-btngroup button').then($buttons => { + const randomIndex = Math.floor(Math.random() * $buttons.length); + cy.wrap($buttons[randomIndex]).click({ force: true }); + }); + } + // Check for any other buttons + else if ($body.find('button').length > 0) { + cy.get('button').first().click({ force: true }); + } + // Try clicking on clickable elements + else if ($body.find('[onclick], .clickable, .jspsych-btn').length > 0) { + cy.get('[onclick], .clickable, .jspsych-btn').first().click({ force: true }); + } + }); + + // Take screenshot after interaction + cy.screenshot(screenshotName); + }; + + // Capture 15 screenshots with interactions + captureWithInteraction('02-after-8s'); + captureWithInteraction('03-after-16s'); + captureWithInteraction('04-after-24s'); + captureWithInteraction('05-after-32s'); + captureWithInteraction('06-after-40s'); + captureWithInteraction('07-after-48s'); + captureWithInteraction('08-after-56s'); + captureWithInteraction('09-after-64s'); + captureWithInteraction('10-after-72s'); + captureWithInteraction('11-after-80s'); + captureWithInteraction('12-after-88s'); + captureWithInteraction('13-after-96s'); + captureWithInteraction('14-after-104s'); + captureWithInteraction('15-final'); + + // Final wait and screenshot + cy.wait(5000); + cy.screenshot('16-very-final'); + }); +}); \ No newline at end of file diff --git a/cypress/e2e/hearts-and-flowers_smart_capture.cy.js b/cypress/e2e/hearts-and-flowers_smart_capture.cy.js new file mode 100644 index 000000000..b56ce0de2 --- /dev/null +++ b/cypress/e2e/hearts-and-flowers_smart_capture.cy.js @@ -0,0 +1,276 @@ +describe('Task Screenshot Capture', () => { + it('should capture screenshots with smart interactions', () => { + const taskName = 'hearts-and-flowers'; + const interactionStrategy = 'spatial_response'; + const screenshotInterval = 8 * 1000; + const maxDuration = 180 * 1000; + let screenshotCount = 0; + + // Mock fullscreen API + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context for tasks that need it + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ connect: () => {}, start: () => {}, stop: () => {} }), + createGain: () => ({ connect: () => {}, gain: { value: 0 } }), + destination: {}, + currentTime: 0 + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Start interaction loop + const startTime = Date.now(); + + function performTaskSpecificInteraction() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Task-specific interaction logic + cy.then(() => { + switch (interactionStrategy) { + case 'afc_multi_choice': + return performAFCInteraction(); + case 'afc_with_slider': + return performMathInteraction(); + case 'corsi_blocks': + return performMemoryGameInteraction(); + case 'spatial_response': + return performSpatialInteraction(); + case 'binary_choice': + return performBinaryChoiceInteraction(); + case 'instruction_only': + return performInstructionInteraction(); + default: + return performGenericInteraction(); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + performTaskSpecificInteraction(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // AFC (Alternative Forced Choice) interaction for most tasks + function performAFCInteraction() { + // Look for multiple choice buttons (2-4 options) + cy.get('body').then($body => { + // Try different button selectors in order of preference + const selectors = [ + '#jspsych-html-multi-response-btngroup button', // Most common + '.jspsych-btn', // Standard jsPsych buttons + 'button[data-choice]', // Choice buttons + '.lev-response-row button', // Levante framework buttons + 'button:not(.replay):not(#replay-btn-revisited)', // Any button except replay + 'button' // Fallback to any button + ]; + + for (let selector of selectors) { + const buttons = $body.find(selector); + if (buttons.length >= 2 && buttons.length <= 4) { + // Found multi-choice buttons, click a random one + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + return; + } + } + + // Fallback: look for any clickable element + performGenericInteraction(); + }); + } + + // Math task interaction (includes sliders) + function performMathInteraction() { + cy.get('body').then($body => { + // Check for slider first + if ($body.find('input[type="range"], .slider').length > 0) { + cy.get('input[type="range"], .slider').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + min; + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + + // Look for submit/continue button after slider + cy.wait(500); + cy.get('button').contains(/continue|submit|next|ok/i).click({ force: true }); + } else { + // No slider, use AFC interaction + performAFCInteraction(); + } + }); + } + + // Memory game interaction (Corsi blocks) + function performMemoryGameInteraction() { + cy.get('body').then($body => { + // Look for Corsi blocks or memory game elements + const corsiSelectors = [ + '.corsi-block', + '.memory-block', + '[data-block]', + '.block', + 'div[style*="background-color"]:not(.instructions)' + ]; + + let foundBlocks = false; + for (let selector of corsiSelectors) { + const blocks = $body.find(selector); + if (blocks.length > 0) { + // Click a random block + const randomIndex = Math.floor(Math.random() * blocks.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + foundBlocks = true; + break; + } + } + + if (!foundBlocks) { + // Look for OK/Continue buttons (common in memory game instructions) + const buttonSelectors = [ + 'button:contains("OK")', + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("Start")', + '.jspsych-btn' + ]; + + for (let selector of buttonSelectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + } + }); + } + + // Spatial response (Hearts and Flowers) + function performSpatialInteraction() { + cy.get('body').then($body => { + // Look for directional buttons or spatial elements + const spatialSelectors = [ + 'button[data-direction]', + '.direction-button', + 'button:contains("โ†")', + 'button:contains("โ†’")', + 'button:contains("โ†‘")', + 'button:contains("โ†“")', + '.spatial-response button' + ]; + + let foundSpatial = false; + for (let selector of spatialSelectors) { + if ($body.find(selector).length > 0) { + const buttons = $body.find(selector); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + foundSpatial = true; + break; + } + } + + if (!foundSpatial) { + performAFCInteraction(); + } + }); + } + + // Binary choice interaction + function performBinaryChoiceInteraction() { + cy.get('body').then($body => { + const buttons = $body.find('button:not(.replay):not(#replay-btn-revisited)'); + if (buttons.length === 2) { + // Exactly 2 buttons, pick randomly + const randomIndex = Math.floor(Math.random() * 2); + cy.get('button:not(.replay):not(#replay-btn-revisited)').eq(randomIndex).click({ force: true }); + } else { + performAFCInteraction(); + } + }); + } + + // Instruction-only interaction + function performInstructionInteraction() { + cy.get('body').then($body => { + // Look for continue/next/OK buttons + const instructionButtons = [ + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("OK")', + 'button:contains("Start")', + '.jspsych-btn' + ]; + + for (let selector of instructionButtons) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + }); + } + + // Generic fallback interaction + function performGenericInteraction() { + cy.get('body').then($body => { + // Try to find any clickable element + const genericSelectors = [ + 'button:not(.replay):not(#replay-btn-revisited):visible', + 'input[type="submit"]:visible', + '[role="button"]:visible', + '.clickable:visible', + 'a:visible' + ]; + + for (let selector of genericSelectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + // Last resort: click somewhere in the middle of the screen + cy.get('body').click(400, 300, { force: true }); + }); + } + + // Start the interaction loop + cy.wait(2000); // Wait for initial load + performTaskSpecificInteraction(); + }); +}); diff --git a/extracted_screenshots/raw_frames/03_after_ok_click.png b/extracted_screenshots/raw_frames/03_after_ok_click.png deleted file mode 100644 index c75b43295..000000000 Binary files a/extracted_screenshots/raw_frames/03_after_ok_click.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/04_after_wait.png b/extracted_screenshots/raw_frames/04_after_wait.png deleted file mode 100644 index 8cf198d14..000000000 Binary files a/extracted_screenshots/raw_frames/04_after_wait.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/05_game_progress_1.png b/extracted_screenshots/raw_frames/05_game_progress_1.png deleted file mode 100644 index 8cf198d14..000000000 Binary files a/extracted_screenshots/raw_frames/05_game_progress_1.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/05_game_progress_2.png b/extracted_screenshots/raw_frames/05_game_progress_2.png deleted file mode 100644 index ae697660a..000000000 Binary files a/extracted_screenshots/raw_frames/05_game_progress_2.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/05_game_progress_3.png b/extracted_screenshots/raw_frames/05_game_progress_3.png deleted file mode 100644 index 1d0b6fdfb..000000000 Binary files a/extracted_screenshots/raw_frames/05_game_progress_3.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/05_game_progress_4.png b/extracted_screenshots/raw_frames/05_game_progress_4.png deleted file mode 100644 index 6a3fe0c71..000000000 Binary files a/extracted_screenshots/raw_frames/05_game_progress_4.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/05_game_progress_5.png b/extracted_screenshots/raw_frames/05_game_progress_5.png deleted file mode 100644 index 483e34410..000000000 Binary files a/extracted_screenshots/raw_frames/05_game_progress_5.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/06_final_screenshot.png b/extracted_screenshots/raw_frames/06_final_screenshot.png deleted file mode 100644 index ad18a12cd..000000000 Binary files a/extracted_screenshots/raw_frames/06_final_screenshot.png and /dev/null differ diff --git a/requirements_ocr.txt b/requirements_ocr.txt new file mode 100644 index 000000000..7630cb72e --- /dev/null +++ b/requirements_ocr.txt @@ -0,0 +1,4 @@ +pytesseract>=0.3.10 +Pillow>=9.0.0 +opencv-python>=4.5.0 +numpy>=1.21.0 \ No newline at end of file diff --git a/run_comprehensive_tests.sh b/run_comprehensive_tests.sh new file mode 100755 index 000000000..60565da4f --- /dev/null +++ b/run_comprehensive_tests.sh @@ -0,0 +1,314 @@ +#!/bin/bash + +# Comprehensive Levante Tasks Screenshot Capture System +# Enhanced with OCR-based duplicate removal and robust interaction logic +# Fixed to work with task-launcher directory structure + +set -e + +TASKS=( + "egma-math" + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Correct paths for task-launcher structure +TASK_LAUNCHER_DIR="task-launcher" +CYPRESS_DIR="${TASK_LAUNCHER_DIR}/cypress/e2e" +SCREENSHOTS_DIR="${TASK_LAUNCHER_DIR}/cypress/screenshots" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if server is running +check_server() { + log "Checking if server is running on port 8081..." + if curl -s http://localhost:8081 > /dev/null; then + success "Server is running on port 8081" + return 0 + else + error "Server is not running on port 8081" + return 1 + fi +} + +# Create enhanced Cypress test file for a task +create_test_file() { + local task=$1 + local test_file="${CYPRESS_DIR}/${task}_capture.cy.js" + + cat > "$test_file" << 'EOF' +describe('TASK_NAME Screenshot Capture', () => { + beforeEach(() => { + // Handle any uncaught exceptions + cy.on('uncaught:exception', (err, runnable) => { + // Return false to prevent any errors from failing the test + return false; + }); + }); + + it('should capture comprehensive screenshots with enhanced interactions', () => { + cy.visit('http://localhost:8081/?task=TASK_NAME&skip=true&skipInstructions=true&heavyInstructions=false'); + + // Wait for initial load + cy.wait(3000); + + // Enhanced interaction strategies + const interactionStrategies = [ + // Strategy 1: Navigation buttons + () => { + cy.get('body').then($body => { + const buttons = ['Continue', 'OK', 'Next', 'Start', 'Begin', 'Got it']; + for (const buttonText of buttons) { + if ($body.find(`button:contains("${buttonText}"), [role="button"]:contains("${buttonText}")`).length > 0) { + cy.contains(buttonText).first().click({ force: true }); + return; + } + } + }); + }, + + // Strategy 2: Number line slider interactions + () => { + cy.get('body').then($body => { + if ($body.find('.number-line, [data-testid*="slider"], [role="slider"]').length > 0) { + cy.get('.number-line, [data-testid*="slider"], [role="slider"]').first().then($slider => { + const randomValue = Math.floor(Math.random() * 100); + cy.wrap($slider).click({ force: true }); + cy.wrap($slider).type(`{leftarrow}{leftarrow}{leftarrow}${randomValue}{enter}`, { force: true }); + }); + } + }); + }, + + // Strategy 3: Multiple choice responses + () => { + cy.get('body').then($body => { + const choices = $body.find('[data-testid*="choice"], .choice, .option, input[type="radio"]'); + if (choices.length > 0) { + const randomIndex = Math.floor(Math.random() * choices.length); + cy.wrap(choices.eq(randomIndex)).click({ force: true }); + } + }); + }, + + // Strategy 4: Number inputs + () => { + cy.get('body').then($body => { + if ($body.find('input[type="number"], input[inputmode="numeric"]').length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.get('input[type="number"], input[inputmode="numeric"]').first().clear().type(randomNumber.toString(), { force: true }); + } + }); + }, + + // Strategy 5: Audio response buttons with wait + () => { + cy.get('body').then($body => { + if ($body.find('[data-testid*="audio"], .audio-button, [aria-label*="audio"]').length > 0) { + cy.get('[data-testid*="audio"], .audio-button, [aria-label*="audio"]').first().click({ force: true }); + cy.wait(2000); // Wait for audio + } + }); + }, + + // Strategy 6: Enabled buttons fallback + () => { + cy.get('body').then($body => { + const enabledButtons = $body.find('button:not(:disabled), [role="button"]:not([aria-disabled="true"])'); + if (enabledButtons.length > 0) { + const randomIndex = Math.floor(Math.random() * enabledButtons.length); + cy.wrap(enabledButtons.eq(randomIndex)).click({ force: true }); + } + }); + }, + + // Strategy 7: Data attribute clickables + () => { + cy.get('body').then($body => { + const clickables = $body.find('[data-testid*="click"], [data-cy*="click"], .clickable'); + if (clickables.length > 0) { + const randomIndex = Math.floor(Math.random() * clickables.length); + cy.wrap(clickables.eq(randomIndex)).click({ force: true }); + } + }); + }, + + // Strategy 8: Random button selection + () => { + cy.get('body').then($body => { + const allButtons = $body.find('button, [role="button"]'); + if (allButtons.length > 0) { + const randomIndex = Math.floor(Math.random() * allButtons.length); + cy.wrap(allButtons.eq(randomIndex)).click({ force: true }); + } + }); + }, + + // Strategy 9: Keyboard inputs + () => { + cy.get('body').type('{enter}', { force: true }); + cy.wait(500); + cy.get('body').type(' ', { force: true }); // Space bar + } + ]; + + // Capture screenshots with enhanced interactions + for (let i = 0; i < 48; i++) { // 8 minutes worth at 10-second intervals + cy.screenshot(`TASK_NAME-screenshot-${String(i + 1).padStart(3, '0')}`, { + capture: 'viewport', + overwrite: true + }); + + // Apply multiple interaction strategies + const strategyIndex = i % interactionStrategies.length; + try { + interactionStrategies[strategyIndex](); + } catch (e) { + // Continue if interaction fails + } + + // Additional random interaction + if (Math.random() > 0.5) { + try { + const randomStrategy = Math.floor(Math.random() * interactionStrategies.length); + interactionStrategies[randomStrategy](); + } catch (e) { + // Continue if interaction fails + } + } + + cy.wait(10000); // 10 seconds between screenshots + } + }); +}); +EOF + + # Replace TASK_NAME with actual task name + sed -i "s/TASK_NAME/${task}/g" "$test_file" + success "Created test file: $test_file" +} + +# Run OCR cleanup for a task +run_ocr_cleanup() { + local task=$1 + local task_screenshots_dir="${SCREENSHOTS_DIR}/${task}_capture.cy.js" + + if [ -d "$task_screenshots_dir" ]; then + log "Running OCR cleanup for $task..." + if [ -f "cleanup_screenshots_ocr.py" ]; then + python3 cleanup_screenshots_ocr.py "$task_screenshots_dir" + success "OCR cleanup completed for $task" + else + warning "OCR cleanup script not found, skipping cleanup for $task" + fi + else + warning "No screenshots directory found for $task" + fi +} + +# Main execution +main() { + log "Starting comprehensive Levante tasks screenshot capture..." + + # Check if server is running + if ! check_server; then + error "Please start the development server first:" + error "cd task-launcher && npm run dev" + exit 1 + fi + + # Create cypress directories if they don't exist + mkdir -p "$CYPRESS_DIR" + mkdir -p "$SCREENSHOTS_DIR" + + # Create backup directory with timestamp + BACKUP_DIR="screenshots_backup_$(date +%Y%m%d_%H%M%S)" + mkdir -p "$BACKUP_DIR" + + success "Starting capture for ${#TASKS[@]} tasks..." + success "Working directory: $(pwd)" + success "Cypress directory: $CYPRESS_DIR" + success "Screenshots directory: $SCREENSHOTS_DIR" + + for task in "${TASKS[@]}"; do + log "Processing task: $task" + + # Create test file + create_test_file "$task" + + # Run Cypress test with timeout from task-launcher directory + log "Running Cypress test for $task (10-minute timeout)..." + if (cd "$TASK_LAUNCHER_DIR" && timeout 600 npx cypress run --spec "cypress/e2e/${task}_capture.cy.js" --headless); then + success "Cypress test completed for $task" + else + warning "Cypress test timed out or failed for $task, continuing..." + fi + + # Run OCR cleanup + run_ocr_cleanup "$task" + + # Copy screenshots to backup + if [ -d "${SCREENSHOTS_DIR}/${task}_capture.cy.js" ]; then + cp -r "${SCREENSHOTS_DIR}/${task}_capture.cy.js" "$BACKUP_DIR/" + success "Backed up screenshots for $task" + fi + + log "Completed processing for $task" + echo "----------------------------------------" + done + + success "All tasks completed!" + log "Screenshots saved in: $SCREENSHOTS_DIR" + log "Backup created in: $BACKUP_DIR" + + # Generate summary report + echo "=== CAPTURE SUMMARY ===" > capture_summary.txt + echo "Date: $(date)" >> capture_summary.txt + echo "Tasks processed: ${#TASKS[@]}" >> capture_summary.txt + echo "" >> capture_summary.txt + + for task in "${TASKS[@]}"; do + task_dir="${SCREENSHOTS_DIR}/${task}_capture.cy.js" + if [ -d "$task_dir" ]; then + count=$(find "$task_dir" -name "*.png" | wc -l) + echo "$task: $count screenshots" >> capture_summary.txt + else + echo "$task: No screenshots found" >> capture_summary.txt + fi + done + + success "Summary report saved to: capture_summary.txt" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/=1.21.0 b/task-launcher/=1.21.0 new file mode 100644 index 000000000..e69de29bb diff --git a/task-launcher/backup_task_screenshots.sh b/task-launcher/backup_task_screenshots.sh new file mode 100755 index 000000000..cb4ac2d33 --- /dev/null +++ b/task-launcher/backup_task_screenshots.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Robust Task Screenshot Backup System +# Usage: ./backup_task_screenshots.sh + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Example: $0 egma-math" + exit 1 +fi + +TASK_NAME=$1 +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +SOURCE_DIR="cypress/screenshots/${TASK_NAME}_complete.cy.js" +BACKUP_ROOT="task_screenshots_backup" +TASK_BACKUP_DIR="$BACKUP_ROOT/${TASK_NAME}_${TIMESTAMP}" + +echo "๐Ÿ”’ TASK SCREENSHOT BACKUP SYSTEM" +echo "=================================" +echo "๐Ÿ“‹ Task: $TASK_NAME" +echo "๐Ÿ“ Source: $SOURCE_DIR" +echo "๐Ÿ’พ Backup: $TASK_BACKUP_DIR" +echo "" + +# Check if source directory exists +if [ ! -d "$SOURCE_DIR" ]; then + echo "โŒ Error: Source directory not found: $SOURCE_DIR" + exit 1 +fi + +# Create backup directory structure +mkdir -p "$BACKUP_ROOT" +mkdir -p "$TASK_BACKUP_DIR" + +# Count screenshots before backup +TOTAL_SCREENSHOTS=$(find "$SOURCE_DIR" -name "*.png" | wc -l) +if [ $TOTAL_SCREENSHOTS -eq 0 ]; then + echo "โš ๏ธ Warning: No screenshots found in $SOURCE_DIR" + exit 1 +fi + +echo "๐Ÿ“ธ Found $TOTAL_SCREENSHOTS screenshots to backup" + +# Copy all screenshots and reports +echo "๐Ÿ”„ Copying screenshots..." +cp -r "$SOURCE_DIR"/* "$TASK_BACKUP_DIR/" 2>/dev/null + +# Verify backup +BACKED_UP_SCREENSHOTS=$(find "$TASK_BACKUP_DIR" -name "*.png" | wc -l) +BACKED_UP_DUPLICATES=$(find "$TASK_BACKUP_DIR/duplicates_backup" -name "*.png" 2>/dev/null | wc -l || echo "0") + +echo "โœ… Backup completed successfully!" +echo "" +echo "๐Ÿ“Š Backup Summary:" +echo " Original screenshots: $TOTAL_SCREENSHOTS" +echo " Backed up screenshots: $BACKED_UP_SCREENSHOTS" +echo " Duplicates preserved: $BACKED_UP_DUPLICATES" +echo " Backup location: $TASK_BACKUP_DIR" + +# Create backup manifest +cat > "$TASK_BACKUP_DIR/backup_manifest.txt" << EOF +Task Screenshot Backup Manifest +=============================== +Task Name: $TASK_NAME +Backup Date: $(date) +Original Directory: $SOURCE_DIR +Backup Directory: $TASK_BACKUP_DIR +Total Screenshots: $TOTAL_SCREENSHOTS +Backed Up Screenshots: $BACKED_UP_SCREENSHOTS +Duplicates Preserved: $BACKED_UP_DUPLICATES + +Files Included: +$(ls -la "$TASK_BACKUP_DIR" | grep -v "^total") +EOF + +echo "๐Ÿ“‹ Manifest created: $TASK_BACKUP_DIR/backup_manifest.txt" +echo "" +echo "๐Ÿ” Screenshots are now safely preserved and can be restored anytime!" +echo "๐Ÿ”„ Original directory remains untouched for continued testing." \ No newline at end of file diff --git a/task-launcher/capture_all_tasks_complete.sh b/task-launcher/capture_all_tasks_complete.sh new file mode 100755 index 000000000..73fcb63e6 --- /dev/null +++ b/task-launcher/capture_all_tasks_complete.sh @@ -0,0 +1,308 @@ +#!/bin/bash + +# Complete Task Screenshot Capture & OCR Processing +# Captures all 12 tasks, saves screenshots, and extracts unique ones using OCR + +set -e + +# Configuration +SCREENSHOT_INTERVAL=5 # seconds between screenshots +TASK_DURATION=240 # 4 minutes per task (48 screenshots each) +SCREENSHOT_COUNT=48 # Total screenshots per task +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +RESULTS_DIR="unique_screenshots_${TIMESTAMP}" + +# All 12 tasks +TASKS="hearts-and-flowers egma-math matrix-reasoning mental-rotation memory-game same-different-selection trog vocab theory-of-mind intro roar-inference adult-reasoning" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +info() { + echo -e "${CYAN}[INFO]${NC} $1" +} + +# Function to ensure webpack server is running +ensure_webpack_server() { + log "Checking webpack server status..." + + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is already running" + return 0 + fi + + log "Starting webpack dev server on port 8080..." + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + log "Waiting for webpack server to start..." + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is ready!" + return 0 + fi + echo -n "." + sleep 2 + done + + error "Webpack server failed to start" + return 1 +} + +# Function to generate Cypress test for a task +generate_task_test() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_complete.cy.js" + + cat > "$test_file" << 'EOF' +describe('TASK_NAME Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=TASK_NAME'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture SCREENSHOT_COUNT screenshots over TASK_DURATION seconds (every SCREENSHOT_INTERVAL seconds) + for (let i = 1; i <= SCREENSHOT_COUNT; i++) { + // Wait SCREENSHOT_INTERVAL seconds + cy.wait(SCREENSHOT_INTERVAL * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); +EOF + + # Replace placeholders with actual values + sed -i "s/TASK_NAME/${task_name}/g" "$test_file" + sed -i "s/TASK_DURATION/${TASK_DURATION}/g" "$test_file" + sed -i "s/SCREENSHOT_INTERVAL/${SCREENSHOT_INTERVAL}/g" "$test_file" + sed -i "s/SCREENSHOT_COUNT/${SCREENSHOT_COUNT}/g" "$test_file" + + success "Generated test file: $test_file" +} + +# Function to run Cypress test for a task +run_task_capture() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_complete.cy.js" + + log "Running Cypress test for $task_name..." + + # Run with timeout + if timeout ${TASK_DURATION}s npx cypress run --spec "$test_file" --browser electron --config video=false; then + success "Cypress test completed for $task_name" + return 0 + else + warn "Cypress test for $task_name completed with timeout (expected)" + return 0 + fi +} + +# Function to process screenshots with OCR +process_screenshots_ocr() { + local task_name="$1" + local screenshot_dir="cypress/screenshots/${task_name}_complete.cy.js" + + if [ ! -d "$screenshot_dir" ]; then + warn "No screenshots directory found for $task_name" + return 1 + fi + + local screenshot_count=$(find "$screenshot_dir" -name "*.png" | wc -l) + if [ "$screenshot_count" -eq 0 ]; then + warn "No screenshots found for $task_name" + return 1 + fi + + log "Processing $screenshot_count screenshots for $task_name with OCR..." + + # Run OCR cleanup + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + local unique_count=$(find "$screenshot_dir" -name "*.png" -not -path "*/duplicates_backup/*" | wc -l) + success "OCR processing completed for $task_name: $screenshot_count โ†’ $unique_count unique screenshots" + + # Copy unique screenshots to results directory + local task_results_dir="$RESULTS_DIR/$task_name" + mkdir -p "$task_results_dir" + find "$screenshot_dir" -name "*.png" -not -path "*/duplicates_backup/*" -exec cp {} "$task_results_dir/" \; + + info "Unique screenshots saved to: $task_results_dir" + return 0 + else + error "OCR processing failed for $task_name" + return 1 + fi +} + +# Function to cleanup +cleanup() { + log "Cleaning up..." + if [ ! -z "$WEBPACK_PID" ]; then + log "Stopping webpack server (PID: $WEBPACK_PID)..." + kill $WEBPACK_PID 2>/dev/null || true + wait $WEBPACK_PID 2>/dev/null || true + fi +} + +# Set trap for cleanup +trap cleanup EXIT + +# Main execution +main() { + log "๐Ÿš€ Starting Complete Task Screenshot Capture & OCR Processing" + log "Tasks to process: $(echo $TASKS | wc -w) tasks" + log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + log "Results will be saved to: $RESULTS_DIR" + + # Create results directory + mkdir -p "$RESULTS_DIR" + + # Ensure webpack server is running + if ! ensure_webpack_server; then + error "Failed to start webpack server" + exit 1 + fi + + # Process each task + local task_num=1 + local total_tasks=$(echo $TASKS | wc -w) + local successful_tasks=0 + local total_unique_screenshots=0 + + for task in $TASKS; do + log "" + log "๐Ÿ“‹ Processing task $task_num/$total_tasks: $task" + log "============================================" + + # Generate test + if generate_task_test "$task"; then + # Run capture + if run_task_capture "$task"; then + # Process with OCR + if process_screenshots_ocr "$task"; then + successful_tasks=$((successful_tasks + 1)) + local task_unique_count=$(find "$RESULTS_DIR/$task" -name "*.png" | wc -l) + total_unique_screenshots=$((total_unique_screenshots + task_unique_count)) + success "โœ… Task $task completed successfully ($task_unique_count unique screenshots)" + else + error "โŒ Task $task failed during OCR processing" + fi + else + error "โŒ Task $task failed during capture" + fi + else + error "โŒ Task $task failed during test generation" + fi + + task_num=$((task_num + 1)) + + # Small delay between tasks + sleep 2 + done + + # Final summary + log "" + log "๐ŸŽ‰ FINAL SUMMARY" + log "================" + success "Successful tasks: $successful_tasks/$total_tasks" + success "Total unique screenshots: $total_unique_screenshots" + success "Results saved in: $RESULTS_DIR" + + # Create summary file + cat > "$RESULTS_DIR/SUMMARY.txt" << EOF +Task Screenshot Capture Summary +Generated: $(date) + +Configuration: +- Screenshot interval: ${SCREENSHOT_INTERVAL} seconds +- Task duration: ${TASK_DURATION} seconds +- Total tasks attempted: $total_tasks +- Successful tasks: $successful_tasks +- Total unique screenshots: $total_unique_screenshots + +Results Directory Structure: +$RESULTS_DIR/ +$(find "$RESULTS_DIR" -type d -name "*" | sed 's|^| |') + +Unique Screenshots by Task: +$(for task in $TASKS; do + if [ -d "$RESULTS_DIR/$task" ]; then + count=$(find "$RESULTS_DIR/$task" -name "*.png" | wc -l) + echo " $task: $count screenshots" + fi +done) +EOF + + success "Summary saved to: $RESULTS_DIR/SUMMARY.txt" + + if [ "$successful_tasks" -gt 0 ]; then + log "" + info "๐Ÿ” To view your unique screenshots:" + info " cd $RESULTS_DIR" + info " ls -la" + log "" + fi +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/capture_all_tasks_final.sh b/task-launcher/capture_all_tasks_final.sh new file mode 100755 index 000000000..43a5db0fe --- /dev/null +++ b/task-launcher/capture_all_tasks_final.sh @@ -0,0 +1,213 @@ +#!/bin/bash +# Final comprehensive script to capture screenshots for all 12 tasks + +echo "๐ŸŽฏ COMPREHENSIVE TASK SCREENSHOT CAPTURE" +echo "========================================" + +# Create timestamped backup directory +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_DIR="all_tasks_final_$TIMESTAMP" +echo "๐Ÿ“ Results will be preserved in: $BACKUP_DIR" + +# All 12 tasks +TASKS=( + "egma-math" + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Global variables +SERVER_PID="" +successful=0 +failed=0 + +# Function to start webpack dev server +start_server() { + echo "๐Ÿš€ Starting webpack dev server..." + + # Kill any existing servers + pkill -f "webpack serve" 2>/dev/null || true + lsof -ti:8080 | xargs kill -9 2>/dev/null || true + sleep 3 + + # Start new server + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + SERVER_PID=$! + + # Wait for server + echo "โณ Waiting for server to start..." + sleep 15 + + # Check if server is running + if curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โœ… Server is running on port 8080" + return 0 + else + echo "โŒ Server failed to start" + return 1 + fi +} + +# Function to capture screenshots for a task +capture_task() { + local task_name=$1 + local test_file="cypress/e2e/${task_name//-/_}_capture.cy.js" + + echo "" + echo "==================== ${task_name^^} ====================" + echo "๐Ÿ“ธ Capturing screenshots for: $task_name" + + # Create Cypress test file + mkdir -p cypress/e2e + cat > "$test_file" << EOF +describe('${task_name^} Screenshot Capture', () => { + it('captures screenshots from ${task_name}', () => { + // Visit with fullscreen mocking + cy.visit('http://localhost:8080/?task=${task_name}', { + timeout: 30000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Take initial screenshot + cy.screenshot('01-initial-load'); + cy.wait(5000); + + // Take screenshots every 10 seconds for 2 minutes (12 screenshots) + for (let i = 1; i <= 12; i++) { + cy.wait(10000); + cy.screenshot(\`\${(i+1).toString().padStart(2, '0')}-interval-\${i}\`); + + // Try to interact with common elements + cy.get('body').then((\$body) => { + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } else if (\$body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } else if (\$body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + cy.screenshot('14-final'); + }); +}); +EOF + + # Run Cypress test with timeout + echo "๐Ÿƒ Running Cypress test for $task_name..." + if timeout 180 npx cypress run --spec "$test_file" --record false; then + echo "โœ… $task_name completed successfully" + + # Check screenshots + local screenshot_dir="cypress/screenshots/${task_name//-/_}_capture.cy.js" + if [ -d "$screenshot_dir" ]; then + local count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + echo "๐Ÿ“ธ Captured $count screenshots for $task_name" + fi + + return 0 + else + echo "โŒ $task_name failed or timed out" + return 1 + fi +} + +# Cleanup function +cleanup() { + echo "" + echo "๐Ÿ›‘ Stopping dev server..." + kill $SERVER_PID 2>/dev/null || true + pkill -f "webpack serve" 2>/dev/null || true +} + +# Set trap to cleanup on exit +trap cleanup EXIT + +# Start the dev server +if ! start_server; then + echo "๐Ÿ’ฅ Cannot start dev server. Exiting." + exit 1 +fi + +# Run screenshot capture for all tasks +echo "" +echo "๐Ÿš€ Starting screenshot capture for all tasks..." + +for task in "${TASKS[@]}"; do + if capture_task "$task"; then + ((successful++)) + else + ((failed++)) + + # Try to restart server if it failed + echo "๐Ÿ”„ Attempting to restart server..." + if ! start_server; then + echo "๐Ÿ’ฅ Server restart failed. Stopping." + break + fi + fi + + # Small delay between tasks + sleep 2 +done + +# Create comprehensive backup +echo "" +echo "๐Ÿ“ฆ Creating comprehensive backup..." +if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots "$BACKUP_DIR" + echo "โœ… All screenshots backed up to: $BACKUP_DIR" + + # Count total screenshots + total_screenshots=$(find "$BACKUP_DIR" -name "*.png" | wc -l) + echo "๐Ÿ“Š Total screenshots captured: $total_screenshots" +fi + +# Final summary +echo "" +echo "==================================================" +echo "๐Ÿ“Š FINAL COMPREHENSIVE SUMMARY" +echo "==================================================" +echo "โœ… Successful tasks: $successful" +echo "โŒ Failed tasks: $failed" +echo "๐Ÿ“ Backup directory: $BACKUP_DIR" +echo "๐ŸŽ‰ Comprehensive task screenshot capture complete!" + +# Show breakdown by task +echo "" +echo "๐Ÿ“‹ Task Breakdown:" +for task in "${TASKS[@]}"; do + screenshot_dir="cypress/screenshots/${task//-/_}_capture.cy.js" + if [ -d "$screenshot_dir" ]; then + count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + echo " $task: $count screenshots" + else + echo " $task: 0 screenshots (failed)" + fi +done + +exit 0 \ No newline at end of file diff --git a/task-launcher/capture_all_tasks_robust.sh b/task-launcher/capture_all_tasks_robust.sh new file mode 100755 index 000000000..4df80cbe9 --- /dev/null +++ b/task-launcher/capture_all_tasks_robust.sh @@ -0,0 +1,227 @@ +#!/bin/bash + +# Robust All Tasks Screenshot Capture Script +# Based on successful memory-game approach + +set -e + +# Configuration +TASKS=("egma-math" "matrix-reasoning" "mental-rotation" "hearts-and-flowers" "memory-game" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") +CAPTURE_DURATION=120 # 2 minutes per task +SCREENSHOT_INTERVAL=10 # 10 seconds between screenshots +TOTAL_SCREENSHOTS=12 # 12 screenshots per task +SERVER_PORT=8080 +BACKUP_DIR="all_tasks_screenshots_$(date +%Y%m%d_%H%M%S)" + +echo "๐Ÿš€ Starting comprehensive task screenshot capture" +echo "๐Ÿ“Š Configuration:" +echo " - Tasks: ${#TASKS[@]}" +echo " - Duration per task: ${CAPTURE_DURATION}s" +echo " - Screenshots per task: ${TOTAL_SCREENSHOTS}" +echo " - Total estimated time: $((${#TASKS[@]} * (CAPTURE_DURATION + 60) / 60)) minutes" +echo "" + +# Function to cleanup processes +cleanup_processes() { + echo "๐Ÿงน Cleaning up processes..." + pkill -f "webpack" 2>/dev/null || true + lsof -ti:${SERVER_PORT} | xargs kill -9 2>/dev/null || true + sleep 3 +} + +# Function to start server +start_server() { + echo "๐Ÿš€ Starting webpack server on port ${SERVER_PORT}..." + npx webpack serve --mode development --env dbmode=development --port ${SERVER_PORT} > webpack.log 2>&1 & + SERVER_PID=$! + + # Wait for server to be ready + echo "โณ Waiting for server to start..." + for i in {1..30}; do + if curl -s http://localhost:${SERVER_PORT} > /dev/null 2>&1; then + echo "โœ… Server is ready" + return 0 + fi + sleep 2 + done + + echo "โŒ Server failed to start" + return 1 +} + +# Function to create task-specific test +create_task_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name}_capture.cy.js" + + cat > "${test_file}" << EOF +describe('${task_name} Screenshot Capture', () => { + it('captures varied screenshots from ${task_name}', () => { + cy.visit('http://localhost:${SERVER_PORT}/?task=${task_name}'); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Initial screenshot + cy.screenshot('01-initial-load'); + + // Wait and try to start the task + cy.wait(3000); + cy.get('body').then((\$body) => { + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } else if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + }); + cy.screenshot('02-after-start'); + + // Take screenshots every ${SCREENSHOT_INTERVAL} seconds with smart interactions + for (let i = 1; i <= ${TOTAL_SCREENSHOTS}; i++) { + cy.wait(${SCREENSHOT_INTERVAL}000); + + // Smart interaction logic for different task types + cy.get('body').then((\$body) => { + // Standard buttons + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if (\$body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Memory game blocks + else if (\$body.find('.jspsych-corsi-block').length > 0) { + cy.get('.jspsych-corsi-block').then(\$blocks => { + const randomIndex = Math.floor(Math.random() * \$blocks.length); + cy.wrap(\$blocks[randomIndex]).click({ force: true }); + }); + } + // Multiple choice buttons (adult-reasoning, etc.) + else if (\$body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then(\$buttons => { + const randomIndex = Math.floor(Math.random() * \$buttons.length); + cy.wrap(\$buttons[randomIndex]).click({ force: true }); + }); + } + // Generic buttons + else if (\$body.find('button').length > 0) { + cy.get('button').then(\$buttons => { + const randomIndex = Math.floor(Math.random() * \$buttons.length); + cy.wrap(\$buttons[randomIndex]).click({ force: true }); + }); + } + // Clickable elements + else if (\$body.find('[onclick]').length > 0) { + cy.get('[onclick]').first().click({ force: true }); + } + }); + + cy.screenshot(\`\${String(i + 2).padStart(2, '0')}-interval-\${i}\`); + } + + // Final screenshot + cy.screenshot(\`\${String(${TOTAL_SCREENSHOTS} + 3).padStart(2, '0')}-final\`); + }); +}); +EOF + + echo "โœ… Created test file: ${test_file}" +} + +# Function to run task capture +run_task_capture() { + local task_name=$1 + echo "" + echo "๐ŸŽฏ Processing task: ${task_name}" + echo "โฑ๏ธ Duration: ${CAPTURE_DURATION}s" + + # Create task-specific test + create_task_test "${task_name}" + + # Run Cypress test + echo "๐Ÿš€ Running Cypress test..." + if npx cypress run --spec "cypress/e2e/${task_name}_capture.cy.js" --headless --config defaultCommandTimeout=60000,requestTimeout=60000,responseTimeout=60000; then + echo "โœ… ${task_name} capture completed successfully" + + # Check screenshot results + local screenshot_dir="cypress/screenshots/${task_name}_capture.cy.js" + if [ -d "${screenshot_dir}" ]; then + local count=$(ls -1 "${screenshot_dir}"/*.png 2>/dev/null | wc -l) + echo "๐Ÿ“ธ Captured ${count} screenshots" + + # Run OCR cleanup + echo "๐Ÿ” Running OCR cleanup..." + if python3 cleanup_screenshots_ocr.py "${screenshot_dir}/" --execute; then + local remaining=$(ls -1 "${screenshot_dir}"/*.png 2>/dev/null | wc -l) + echo "โœจ OCR cleanup complete: ${remaining} unique screenshots retained" + else + echo "โš ๏ธ OCR cleanup failed, keeping all screenshots" + fi + else + echo "โš ๏ธ No screenshots found for ${task_name}" + fi + else + echo "โŒ ${task_name} capture failed" + fi + + # Clean up test file + rm -f "cypress/e2e/${task_name}_capture.cy.js" +} + +# Main execution +echo "๐Ÿงน Initial cleanup..." +cleanup_processes + +# Start server +if ! start_server; then + echo "โŒ Failed to start server. Exiting." + exit 1 +fi + +# Create backup directory +mkdir -p "${BACKUP_DIR}" + +# Process each task +for task in "${TASKS[@]}"; do + run_task_capture "${task}" + + # Copy results to backup + if [ -d "cypress/screenshots/${task}_capture.cy.js" ]; then + cp -r "cypress/screenshots/${task}_capture.cy.js" "${BACKUP_DIR}/" + echo "๐Ÿ’พ Backed up ${task} results to ${BACKUP_DIR}/" + fi + + # Brief pause between tasks + sleep 5 +done + +# Final cleanup +echo "" +echo "๐Ÿงน Final cleanup..." +cleanup_processes + +# Summary +echo "" +echo "๐ŸŽ‰ ALL TASKS COMPLETED!" +echo "๐Ÿ“ Results backed up to: ${BACKUP_DIR}/" +echo "๐Ÿ“Š Summary:" +for task in "${TASKS[@]}"; do + if [ -d "cypress/screenshots/${task}_capture.cy.js" ]; then + local unique_count=$(ls -1 "cypress/screenshots/${task}_capture.cy.js"/*.png 2>/dev/null | wc -l) + local backup_count=$(ls -1 "cypress/screenshots/${task}_capture.cy.js/duplicates_backup"/*.png 2>/dev/null | wc -l) + echo " ${task}: ${unique_count} unique screenshots (${backup_count} duplicates in backup)" + else + echo " ${task}: No screenshots captured" + fi +done + +echo "" +echo "โœ… Screenshot capture complete for all ${#TASKS[@]} tasks!" \ No newline at end of file diff --git a/task-launcher/capture_progressing_tasks.sh b/task-launcher/capture_progressing_tasks.sh new file mode 100755 index 000000000..e51e24a8d --- /dev/null +++ b/task-launcher/capture_progressing_tasks.sh @@ -0,0 +1,207 @@ +#!/bin/bash + +# Simple Task Screenshot Capture with Proper Progression +# Focuses on actually advancing through tasks instead of getting stuck + +# Configuration +SCREENSHOT_INTERVAL=6 # seconds between screenshots +TASK_DURATION=240 # 4 minutes per task +TASKS="hearts-and-flowers memory-game matrix-reasoning egma-math" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to generate a working Cypress test +generate_progressing_test() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_progressing.cy.js" + + log "Generating progressing test for $task_name..." + + cat > "$test_file" << EOF +describe('$task_name Task Progression Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = '$task_name'; + const screenshotInterval = $SCREENSHOT_INTERVAL * 1000; + const maxDuration = $TASK_DURATION * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(\`http://localhost:8080/?task=\${taskName}\`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ connect: () => {}, start: () => {}, stop: () => {} }), + createGain: () => ({ connect: () => {}, gain: { value: 0 } }), + destination: {}, + currentTime: 0 + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(\`00-start\`, { capture: 'viewport' }); + screenshotCount++; + + // Progressive screenshot and interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-step-\${screenshotCount - 1}\`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction to progress through task + cy.get('body').then(\$body => { + // Priority 1: Look for "OK" or "Continue" buttons (instructions) + if (\$body.find('button:contains("OK"), button:contains("Continue"), button:contains("Next")').length > 0) { + cy.get('button:contains("OK"), button:contains("Continue"), button:contains("Next")').first().click(); + } + // Priority 2: Look for Corsi blocks (memory game) + else if (\$body.find('.corsi-block, .memory-block, [class*="corsi"], [class*="block"]').length > 0) { + cy.get('.corsi-block, .memory-block, [class*="corsi"], [class*="block"]').first().click(); + } + // Priority 3: Look for multiple choice buttons + else if (\$body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = \$body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click(); + } + // Priority 4: Look for spatial response buttons (hearts-and-flowers) + else if (\$body.find('button[data-response], .response-button, [class*="response"]').length > 0) { + const buttons = \$body.find('button[data-response], .response-button, [class*="response"]'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button[data-response], .response-button, [class*="response"]').eq(randomIndex).click(); + } + // Priority 5: Look for slider inputs (math tasks) + else if (\$body.find('input[type="range"], .slider, [class*="slider"]').length > 0) { + cy.get('input[type="range"], .slider, [class*="slider"]').first().then(\$slider => { + const min = \$slider.attr('min') || 0; + const max = \$slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap(\$slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 6: Look for any clickable buttons + else if (\$body.find('button:not([disabled]), .jspsych-btn').length > 0) { + const buttons = \$body.find('button:not([disabled]), .jspsych-btn'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled]), .jspsych-btn').eq(randomIndex).click(); + } + // Priority 7: Look for clickable elements + else if (\$body.find('[onclick], [data-click], .clickable').length > 0) { + cy.get('[onclick], [data-click], .clickable').first().click(); + } + // Fallback: Click anywhere on screen to potentially advance + else { + cy.get('body').click(500, 300); + } + }); + + // Wait and continue + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(2000).then(() => { + captureAndInteract(); + }); + }); +}); +EOF + + success "Generated test file: $test_file" +} + +# Main execution +log "Starting progressing task screenshot capture..." +log "Tasks to capture: $TASKS" +log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + +# Check if webpack server is running +if ! curl -s http://localhost:8080 > /dev/null 2>&1; then + error "Webpack server not running on port 8080. Please start it first with: npm run dev" + exit 1 +fi + +success "Webpack server is ready!" + +# Process each task +task_count=0 +for task in $TASKS; do + task_count=$((task_count + 1)) + log "Processing task $task_count: $task" + + # Generate test + generate_progressing_test "$task" + + # Run Cypress test + log "Running Cypress test for $task..." + if timeout $((TASK_DURATION + 60)) npx cypress run --spec "cypress/e2e/${task}_progressing.cy.js" --browser electron; then + success "Cypress test completed for $task" + + # Count screenshots + screenshot_dir="cypress/screenshots/${task}_progressing.cy.js" + if [[ -d "$screenshot_dir" ]]; then + screenshot_count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + success "Task $task: $screenshot_count screenshots captured" + + # Run OCR cleanup + log "Running OCR cleanup for $task..." + if python3 cleanup_screenshots_ocr.py --directory "$screenshot_dir" --execute; then + success "OCR cleanup completed for $task" + else + error "OCR cleanup failed for $task" + fi + else + error "No screenshots found for $task" + fi + else + error "Cypress test failed or timed out for $task" + fi + + log "Completed task $task" + echo "----------------------------------------" +done + +log "All tasks completed!" \ No newline at end of file diff --git a/task-launcher/capture_remaining.sh b/task-launcher/capture_remaining.sh new file mode 100644 index 000000000..82153273b --- /dev/null +++ b/task-launcher/capture_remaining.sh @@ -0,0 +1,55 @@ +#!/bin/bash +echo "๐ŸŽฏ CAPTURING REMAINING TASKS" +echo "============================" + +# Kill any existing processes +pkill -f "webpack serve" 2>/dev/null || true +lsof -ti:8080 | xargs kill -9 2>/dev/null || true +sleep 2 + +# Start server +echo "๐Ÿš€ Starting server..." +npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & +SERVER_PID=$! +sleep 15 + +# Check server +if ! curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โŒ Server failed" + exit 1 +fi +echo "โœ… Server running" + +# Capture memory-game as test +echo "๐Ÿ“ธ Testing with memory-game..." +cat > ../cypress/e2e/memory_game_test.cy.js << 'CYPRESS_EOF' +describe('Memory Game Test', () => { + it('captures memory game screenshots', () => { + cy.visit('http://localhost:8080/?task=memory-game'); + + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + cy.screenshot('01-initial'); + + for (let i = 1; i <= 8; i++) { + cy.wait(10000); + cy.get('body').then(($body) => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + }); + cy.screenshot(`${String(i + 1).padStart(2, '0')}-interval-${i}`); + } + }); +}); +CYPRESS_EOF + +# Run test +timeout 180 npx cypress run --spec "../cypress/e2e/memory_game_test.cy.js" --browser electron --headless + +echo "โœ… Test complete!" diff --git a/task-launcher/cleanup_screenshots_ocr.py b/task-launcher/cleanup_screenshots_ocr.py new file mode 100755 index 000000000..2a93d1a0b --- /dev/null +++ b/task-launcher/cleanup_screenshots_ocr.py @@ -0,0 +1,352 @@ +#!/usr/bin/env python3 +""" +Enhanced screenshot cleanup script using OCR text similarity AND image similarity. +Keeps screenshots that are unique in either text content OR visual content. +""" + +import os +import sys +import argparse +from pathlib import Path +from PIL import Image, ImageChops +import pytesseract +from difflib import SequenceMatcher +import hashlib +import json +from typing import List, Dict, Tuple +import re +import numpy as np + +def install_requirements(): + """Install required packages if not available.""" + try: + import pytesseract + from PIL import Image + import numpy as np + except ImportError: + print("Installing required packages...") + os.system("pip install pillow pytesseract numpy") + print("Please also install tesseract-ocr system package:") + print(" Ubuntu/Debian: sudo apt-get install tesseract-ocr") + print(" macOS: brew install tesseract") + sys.exit(1) + +def extract_top_text(image_path: str, top_fraction: float = 0.5) -> str: + """ + Extract text from the top portion of an image using OCR. + + Args: + image_path: Path to the image file + top_fraction: Fraction of image height to analyze (0.5 = top half) + + Returns: + Extracted text, cleaned and normalized + """ + try: + with Image.open(image_path) as img: + width, height = img.size + + # Crop to top portion + top_height = int(height * top_fraction) + top_img = img.crop((0, 0, width, top_height)) + + # Extract text using OCR + text = pytesseract.image_to_string(top_img, config='--psm 6') + + # Clean and normalize text + text = re.sub(r'\s+', ' ', text.strip()) # Normalize whitespace + text = re.sub(r'[^\w\s\.\,\!\?\-]', '', text) # Remove special chars + text = text.lower() # Convert to lowercase + + return text + + except Exception as e: + print(f"Error processing {image_path}: {e}") + return "" + +def calculate_image_similarity(image_path1: str, image_path2: str, resize_to: int = 64) -> float: + """ + Calculate visual similarity between two images using perceptual hashing. + + Args: + image_path1: Path to first image + image_path2: Path to second image + resize_to: Size to resize images for comparison (smaller = faster, less precise) + + Returns: + Similarity score between 0.0 and 1.0 (1.0 = identical) + """ + try: + with Image.open(image_path1) as img1, Image.open(image_path2) as img2: + # Convert to grayscale and resize for faster comparison + img1_small = img1.convert('L').resize((resize_to, resize_to)) + img2_small = img2.convert('L').resize((resize_to, resize_to)) + + # Convert to numpy arrays + arr1 = np.array(img1_small) + arr2 = np.array(img2_small) + + # Calculate normalized cross-correlation + # Flatten arrays + arr1_flat = arr1.flatten().astype(float) + arr2_flat = arr2.flatten().astype(float) + + # Normalize to mean 0 + arr1_norm = arr1_flat - np.mean(arr1_flat) + arr2_norm = arr2_flat - np.mean(arr2_flat) + + # Calculate correlation coefficient + correlation = np.corrcoef(arr1_norm, arr2_norm)[0, 1] + + # Handle NaN (when std dev is 0) + if np.isnan(correlation): + # If both images are uniform (no variation), check if they're the same + return 1.0 if np.allclose(arr1_flat, arr2_flat) else 0.0 + + # Convert correlation to similarity (0 to 1) + similarity = (correlation + 1) / 2 + return max(0.0, min(1.0, similarity)) + + except Exception as e: + print(f"Error comparing images {image_path1} and {image_path2}: {e}") + return 0.0 + +def calculate_text_similarity(text1: str, text2: str) -> float: + """ + Calculate similarity between two text strings. + + Returns: + Similarity score between 0.0 and 1.0 + """ + if not text1 and not text2: + return 1.0 + if not text1 or not text2: + return 0.0 + + return SequenceMatcher(None, text1, text2).ratio() + +def get_text_hash(text: str) -> str: + """Generate a hash for text content.""" + return hashlib.md5(text.encode()).hexdigest()[:8] + +def analyze_screenshots(directory: str, text_similarity_threshold: float = 0.8, + image_similarity_threshold: float = 0.95) -> Dict: + """ + Analyze screenshots and group by BOTH text and image similarity. + A screenshot is considered a duplicate only if BOTH text AND image are similar. + + Args: + directory: Directory containing screenshots + text_similarity_threshold: Threshold for text similarity (0.8 = 80% similar) + image_similarity_threshold: Threshold for image similarity (0.95 = 95% similar) + + Returns: + Dictionary with analysis results + """ + screenshot_dir = Path(directory) + if not screenshot_dir.exists(): + print(f"Directory {directory} does not exist!") + return {} + + # Find all PNG files + png_files = sorted(list(screenshot_dir.glob("*.png"))) + if not png_files: + print(f"No PNG files found in {directory}") + return {} + + print(f"Found {len(png_files)} screenshots to analyze...") + + # Extract text from all screenshots + screenshot_data = [] + for i, png_file in enumerate(png_files): + print(f"Processing {i+1}/{len(png_files)}: {png_file.name}") + + text = extract_top_text(str(png_file)) + file_size = png_file.stat().st_size + + screenshot_data.append({ + 'path': str(png_file), + 'name': png_file.name, + 'text': text, + 'text_hash': get_text_hash(text), + 'file_size': file_size, + 'text_length': len(text) + }) + + # Group by BOTH text and image similarity + unique_groups = [] + duplicates = [] + + print(f"\nComparing images for visual similarity...") + + for i, current in enumerate(screenshot_data): + print(f"Analyzing {i+1}/{len(screenshot_data)}: {current['name']}") + + # Check if this screenshot is similar to any existing group + is_duplicate = False + + for group in unique_groups: + representative = group['representative'] + + # Calculate both text and image similarity + text_similarity = calculate_text_similarity(current['text'], representative['text']) + image_similarity = calculate_image_similarity(current['path'], representative['path']) + + print(f" vs {representative['name']}: text={text_similarity:.3f}, image={image_similarity:.3f}") + + # Consider duplicate only if BOTH text AND image are highly similar + if (text_similarity >= text_similarity_threshold and + image_similarity >= image_similarity_threshold): + + # This is a duplicate + current['text_similarity'] = text_similarity + current['image_similarity'] = image_similarity + current['duplicate_of'] = representative['name'] + + group['duplicates'].append(current) + duplicates.append(current) + is_duplicate = True + print(f" โ†’ DUPLICATE (both text and image similar)") + break + + if not is_duplicate: + # This is unique, create new group + unique_groups.append({ + 'representative': current, + 'duplicates': [] + }) + print(f" โ†’ UNIQUE") + + return { + 'total_screenshots': len(screenshot_data), + 'unique_groups': len(unique_groups), + 'total_duplicates': len(duplicates), + 'groups': unique_groups, + 'all_screenshots': screenshot_data, + 'thresholds': { + 'text_similarity': text_similarity_threshold, + 'image_similarity': image_similarity_threshold + } + } + +def cleanup_screenshots(directory: str, text_similarity_threshold: float = 0.8, + image_similarity_threshold: float = 0.95, + dry_run: bool = True, backup: bool = True) -> None: + """ + Clean up screenshots by removing duplicates based on BOTH text and image similarity. + + Args: + directory: Directory containing screenshots + text_similarity_threshold: Threshold for text similarity + image_similarity_threshold: Threshold for image similarity + dry_run: If True, only show what would be deleted + backup: If True, move duplicates to backup folder instead of deleting + """ + analysis = analyze_screenshots(directory, text_similarity_threshold, image_similarity_threshold) + + if not analysis: + return + + print(f"\n๐Ÿ“Š Analysis Results:") + print(f" Total screenshots: {analysis['total_screenshots']}") + print(f" Unique groups: {analysis['unique_groups']}") + print(f" Duplicates to remove: {analysis['total_duplicates']}") + print(f" Retention rate: {analysis['unique_groups']}/{analysis['total_screenshots']} ({100*analysis['unique_groups']/analysis['total_screenshots']:.1f}%)") + print(f" Thresholds: textโ‰ฅ{analysis['thresholds']['text_similarity']:.2f}, imageโ‰ฅ{analysis['thresholds']['image_similarity']:.2f}") + + # Show detailed analysis + print(f"\n๐Ÿ“‹ Unique Groups:") + for i, group in enumerate(analysis['groups']): + rep = group['representative'] + text_preview = rep['text'][:60] + "..." if len(rep['text']) > 60 else rep['text'] + print(f" Group {i+1}: {rep['name']}") + print(f" Text: '{text_preview}'") + print(f" Size: {rep['file_size']/1024:.1f}KB") + + if group['duplicates']: + print(f" Duplicates: {len(group['duplicates'])}") + for dup in group['duplicates'][:3]: # Show first 3 duplicates + text_sim = dup.get('text_similarity', 0) + image_sim = dup.get('image_similarity', 0) + print(f" - {dup['name']} (text: {text_sim:.2f}, image: {image_sim:.2f})") + if len(group['duplicates']) > 3: + print(f" ... and {len(group['duplicates'])-3} more") + print() + + if dry_run: + print("๐Ÿ” DRY RUN - No files will be modified") + print(" Use --execute to actually perform cleanup") + return + + # Perform cleanup + backup_dir = None + if backup: + backup_dir = Path(directory) / "duplicates_backup" + backup_dir.mkdir(exist_ok=True) + print(f"๐Ÿ“ Backup directory: {backup_dir}") + + files_processed = 0 + for group in analysis['groups']: + for duplicate in group['duplicates']: + files_processed += 1 + src_path = Path(duplicate['path']) + + if backup: + # Move to backup directory + dst_path = backup_dir / src_path.name + src_path.rename(dst_path) + print(f" Moved: {src_path.name} โ†’ duplicates_backup/") + else: + # Delete file + src_path.unlink() + print(f" Deleted: {src_path.name}") + + print(f"\nโœ… Cleanup complete!") + print(f" Processed: {files_processed} duplicates") + print(f" Remaining: {analysis['unique_groups']} unique screenshots") + + # Save analysis report + report_path = Path(directory) / "cleanup_report.json" + with open(report_path, 'w') as f: + json.dump(analysis, f, indent=2) + print(f" Report saved: {report_path}") + +def main(): + parser = argparse.ArgumentParser(description="Clean up screenshots using OCR text similarity AND image similarity") + parser.add_argument("directory", help="Directory containing screenshots") + parser.add_argument("--text-similarity", type=float, default=0.8, + help="Text similarity threshold (0.0-1.0, default: 0.8)") + parser.add_argument("--image-similarity", type=float, default=0.95, + help="Image similarity threshold (0.0-1.0, default: 0.95)") + parser.add_argument("--execute", action="store_true", + help="Actually perform cleanup (default is dry run)") + parser.add_argument("--no-backup", action="store_true", + help="Delete duplicates instead of moving to backup") + parser.add_argument("--install-deps", action="store_true", + help="Install required dependencies") + + args = parser.parse_args() + + if args.install_deps: + install_requirements() + return + + # Check if tesseract is available + try: + pytesseract.get_tesseract_version() + except Exception: + print("โŒ Tesseract OCR not found!") + print(" Install with: sudo apt-get install tesseract-ocr") + print(" Or use --install-deps flag") + sys.exit(1) + + cleanup_screenshots( + directory=args.directory, + text_similarity_threshold=args.text_similarity, + image_similarity_threshold=args.image_similarity, + dry_run=not args.execute, + backup=not args.no_backup + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/task-launcher/complete_task_capture.sh b/task-launcher/complete_task_capture.sh new file mode 100644 index 000000000..740ad33b3 --- /dev/null +++ b/task-launcher/complete_task_capture.sh @@ -0,0 +1,265 @@ +#!/bin/bash + +# Complete Task Capture Script - Ensures full task completion +# Takes screenshots throughout entire task lifecycle + +set -e + +# Configuration +TASKS=("memory-game" "egma-math" "matrix-reasoning" "mental-rotation" "hearts-and-flowers" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") +SERVER_PORT=8080 +BACKUP_DIR="complete_tasks_screenshots_$(date +%Y%m%d_%H%M%S)" +MAX_TASK_DURATION=600 # 10 minutes max per task + +echo "๐Ÿš€ Complete Task Screenshot Capture" +echo "๐Ÿ“Š Configuration:" +echo " - Tasks: ${#TASKS[@]}" +echo " - Max duration per task: ${MAX_TASK_DURATION}s" +echo " - Server port: ${SERVER_PORT}" +echo "" + +# Function to cleanup processes +cleanup_processes() { + echo "๐Ÿงน Cleaning up processes..." + pkill -f "webpack" 2>/dev/null || true + pkill -f "cypress" 2>/dev/null || true + lsof -ti:${SERVER_PORT} | xargs kill -9 2>/dev/null || true + sleep 3 +} + +# Function to start server +start_server() { + echo "๐Ÿš€ Starting webpack server on port ${SERVER_PORT}..." + npx webpack serve --mode development --env dbmode=development --port ${SERVER_PORT} > webpack.log 2>&1 & + SERVER_PID=$! + + # Wait for server to be ready + echo "โณ Waiting for server to start..." + for i in {1..30}; do + if curl -s http://localhost:${SERVER_PORT} > /dev/null 2>&1; then + echo "โœ… Server is ready" + return 0 + fi + sleep 2 + done + + echo "โŒ Server failed to start" + return 1 +} + +# Function to create complete task test +create_complete_task_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name}_complete.cy.js" + + cat > "${test_file}" << EOF +describe('${task_name} Complete Capture', () => { + it('captures screenshots throughout complete ${task_name} run', () => { + cy.visit('http://localhost:${SERVER_PORT}/?task=${task_name}', { + timeout: 60000 + }); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Initial screenshot + cy.screenshot('00-initial-load'); + cy.wait(2000); + + // Start the task + cy.get('body').then((\$body) => { + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot('01-after-ok'); + } else if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + cy.screenshot('01-after-start'); + } + }); + + cy.wait(3000); + cy.screenshot('02-task-started'); + + // Main task loop - continue until completion + let screenshotCounter = 3; + + // Run task completion loop + const runTaskLoop = () => { + // Check for completion first + cy.get('body').then((\$body) => { + const bodyText = \$body.text().toLowerCase(); + + if (bodyText.includes('thank you') || + bodyText.includes('complete') || + bodyText.includes('finished') || + bodyText.includes('done') || + bodyText.includes('exit')) { + cy.screenshot(\`\${String(screenshotCounter).padStart(2, '0')}-completion\`); + return; // Task completed + } else { + // Task still running - take screenshot and continue + cy.screenshot(\`\${String(screenshotCounter).padStart(2, '0')}-progress\`); + screenshotCounter++; + + // Interact with current screen + cy.get('body').then((\$currentBody) => { + // Handle different interaction types + if (\$currentBody.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if (\$currentBody.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if (\$currentBody.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Memory game blocks + else if (\$currentBody.find('.jspsych-corsi-block').length > 0) { + cy.get('.jspsych-corsi-block').then(\$blocks => { + const randomIndex = Math.floor(Math.random() * \$blocks.length); + cy.wrap(\$blocks[randomIndex]).click({ force: true }); + }); + } + // Multiple choice buttons + else if (\$currentBody.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then(\$buttons => { + const randomIndex = Math.floor(Math.random() * \$buttons.length); + cy.wrap(\$buttons[randomIndex]).click({ force: true }); + }); + } + // Any other buttons + else if (\$currentBody.find('button').length > 0) { + cy.get('button').then(\$buttons => { + const visibleButtons = \$buttons.filter(':visible'); + if (visibleButtons.length > 0) { + const randomIndex = Math.floor(Math.random() * visibleButtons.length); + cy.wrap(visibleButtons[randomIndex]).click({ force: true }); + } + }); + } + }); + + // Wait and continue loop + cy.wait(5000); + + // Recursive call to continue loop (with max depth protection) + if (screenshotCounter < 50) { // Max 50 screenshots per task + runTaskLoop(); + } + } + }); + }; + + // Start the main task loop + runTaskLoop(); + + // Final screenshot + cy.screenshot('99-final'); + }); +}); +EOF + + echo "โœ… Created complete test file: ${test_file}" +} + +# Function to run complete task capture +run_complete_task_capture() { + local task_name=$1 + echo "" + echo "๐ŸŽฏ Processing complete task: ${task_name}" + + # Create complete task test + create_complete_task_test "${task_name}" + + # Run Cypress test with extended timeout + echo "๐Ÿš€ Running complete Cypress test..." + if timeout ${MAX_TASK_DURATION} npx cypress run --spec "cypress/e2e/${task_name}_complete.cy.js" --headless --config defaultCommandTimeout=30000,requestTimeout=30000,responseTimeout=30000,taskTimeout=${MAX_TASK_DURATION}000; then + echo "โœ… ${task_name} complete capture finished" + + # Check results + local screenshot_dir="cypress/screenshots/${task_name}_complete.cy.js" + if [ -d "\${screenshot_dir}" ]; then + local count=\$(ls -1 "\${screenshot_dir}"/*.png 2>/dev/null | wc -l) + echo "๐Ÿ“ธ Captured \${count} screenshots for complete ${task_name} run" + + # Show file sizes to verify content variety + echo "๐Ÿ“Š Screenshot sizes:" + ls -lah "\${screenshot_dir}"/*.png | awk '{print " " \$5 " - " \$9}' | head -10 + if [ \$count -gt 10 ]; then + echo " ... and \$((count - 10)) more" + fi + + # Run OCR cleanup + echo "๐Ÿ” Running OCR cleanup..." + if python3 cleanup_screenshots_ocr.py "\${screenshot_dir}/" --execute; then + local remaining=\$(ls -1 "\${screenshot_dir}"/*.png 2>/dev/null | wc -l) + echo "โœจ OCR cleanup complete: \${remaining} unique screenshots retained" + else + echo "โš ๏ธ OCR cleanup failed, keeping all screenshots" + fi + else + echo "โš ๏ธ No screenshots found for ${task_name}" + fi + else + echo "โŒ ${task_name} capture failed or timed out" + fi + + # Clean up test file + rm -f "cypress/e2e/${task_name}_complete.cy.js" +} + +# Main execution +echo "๐Ÿงน Initial cleanup..." +cleanup_processes + +# Start server +if ! start_server; then + echo "โŒ Failed to start server. Exiting." + exit 1 +fi + +# Create backup directory +mkdir -p "${BACKUP_DIR}" + +# Process each task +for task in "\${TASKS[@]}"; do + run_complete_task_capture "\${task}" + + # Copy results to backup + if [ -d "cypress/screenshots/\${task}_complete.cy.js" ]; then + cp -r "cypress/screenshots/\${task}_complete.cy.js" "\${BACKUP_DIR}/" + echo "๐Ÿ’พ Backed up \${task} results to \${BACKUP_DIR}/" + fi + + # Brief pause between tasks + sleep 3 +done + +# Final cleanup +echo "" +echo "๐Ÿงน Final cleanup..." +cleanup_processes + +# Summary +echo "" +echo "๐ŸŽ‰ ALL COMPLETE TASKS FINISHED!" +echo "๐Ÿ“ Results backed up to: \${BACKUP_DIR}/" +echo "๐Ÿ“Š Summary:" +for task in "\${TASKS[@]}"; do + if [ -d "cypress/screenshots/\${task}_complete.cy.js" ]; then + local unique_count=\$(ls -1 "cypress/screenshots/\${task}_complete.cy.js"/*.png 2>/dev/null | wc -l) + local backup_count=0 + if [ -d "cypress/screenshots/\${task}_complete.cy.js/duplicates_backup" ]; then + backup_count=\$(ls -1 "cypress/screenshots/\${task}_complete.cy.js/duplicates_backup"/*.png 2>/dev/null | wc -l) + fi + echo " \${task}: \${unique_count} unique screenshots (\${backup_count} duplicates in backup)" + else + echo " \${task}: No screenshots captured" + fi +done + +echo "" +echo "โœ… Complete task screenshot capture finished for all \${#TASKS[@]} tasks!" \ No newline at end of file diff --git a/task-launcher/continue_remaining_tasks.sh b/task-launcher/continue_remaining_tasks.sh new file mode 100755 index 000000000..1fc5a2d19 --- /dev/null +++ b/task-launcher/continue_remaining_tasks.sh @@ -0,0 +1,213 @@ +#!/bin/bash +# Continue with remaining tasks after egma-math + +echo "๐Ÿ”„ CONTINUING REMAINING TASKS" +echo "================================" + +# Create timestamped backup directory for all results +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_DIR="remaining_tasks_$TIMESTAMP" +echo "๐Ÿ“ Results will be preserved in: $BACKUP_DIR" + +# Remaining tasks (excluding egma-math which already has 44 screenshots) +TASKS=( + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Global variables +SERVER_PID="" + +# Function to kill any existing servers +cleanup_servers() { + echo "๐Ÿงน Cleaning up existing servers..." + pkill -f "webpack serve" 2>/dev/null || true + pkill -f "port 8080" 2>/dev/null || true + lsof -ti:8080 | xargs kill -9 2>/dev/null || true + sleep 3 +} + +# Function to start webpack dev server +start_dev_server() { + echo "๐Ÿš€ Starting webpack dev server..." + cleanup_servers + + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + SERVER_PID=$! + + echo "โณ Waiting for dev server to start..." + sleep 15 + + if curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โœ… Dev server started successfully on port 8080" + return 0 + else + echo "โŒ Dev server failed to start" + return 1 + fi +} + +# Function to create and run test for a task +run_task_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name//-/_}_complete.cy.js" + + echo "" + echo "==================== ${task_name^^} ====================" + echo "๐Ÿ“ธ Running screenshots for: $task_name" + + # Create Cypress test file with shorter 3-minute duration + cat > "$test_file" << EOF +const ${task_name//-/_}_url = 'http://localhost:8080/?task=${task_name}'; + +describe('${task_name^} Complete Run', () => { + let screenshotCounter = 1; + + function takeScreenshot(description) { + cy.screenshot(\`\${screenshotCounter.toString().padStart(3, '0')}-\${description}\`); + screenshotCounter++; + } + + it('runs complete ${task_name} with screenshots', () => { + // Visit with fullscreen mocking and extended timeout + cy.visit(${task_name//-/_}_url, { + timeout: 60000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 3 minutes with screenshots every 10 seconds + const totalDuration = 3 * 60 * 1000; // 3 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) { + cy.wait(screenshotInterval); + takeScreenshot(\`interval-\${i.toString().padStart(2, '0')}\`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then((\$body) => { + // Click OK buttons if they exist + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Click Continue buttons if they exist + if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Click Next buttons if they exist + if (\$body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Click Start buttons if they exist + if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } + // Click any visible buttons as fallback + if (\$body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + takeScreenshot('final-state'); + }); +}); +EOF + + # Run Cypress test + echo "๐Ÿƒ Running Cypress test..." + if timeout 300 npx cypress run --spec "$test_file" --record false; then + echo "โœ… $task_name screenshots completed" + + # Run OCR cleanup (preserves duplicates in backup folder) + local screenshot_dir="cypress/screenshots/${task_name//-/_}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + echo "๐Ÿ” Running OCR cleanup for $task_name..." + if python3 ../cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + echo "โœ… $task_name cleanup complete" + else + echo "โš ๏ธ $task_name cleanup had issues (continuing anyway)" + fi + fi + return 0 + else + echo "โŒ $task_name test failed" + return 1 + fi +} + +# Cleanup function +cleanup() { + echo "" + echo "๐Ÿ›‘ Stopping dev server..." + kill $SERVER_PID 2>/dev/null || true + cleanup_servers +} + +# Set trap to cleanup on exit +trap cleanup EXIT + +# Start the dev server +if ! start_dev_server; then + echo "๐Ÿ’ฅ Cannot start dev server. Exiting." + exit 1 +fi + +# Run tests for remaining tasks +successful=0 +failed=0 + +for task in "${TASKS[@]}"; do + if run_task_test "$task"; then + ((successful++)) + else + ((failed++)) + fi + + # Small delay between tasks + sleep 3 +done + +# Create comprehensive backup of all results +echo "" +echo "๐Ÿ“ฆ Creating comprehensive backup..." +if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots "$BACKUP_DIR" + echo "โœ… All screenshots backed up to: $BACKUP_DIR" +fi + +# Final summary +echo "" +echo "==================================================" +echo "๐Ÿ“Š CONTINUATION SUMMARY REPORT" +echo "==================================================" +echo "โœ… Successful tasks: $successful" +echo "โŒ Failed tasks: $failed" +echo "๐Ÿ“ Backup directory: $BACKUP_DIR" +echo "๐Ÿ“‹ Note: egma-math already completed with 44 screenshots" +echo "๐ŸŽ‰ Remaining task screenshot capture complete!" + +exit 0 \ No newline at end of file diff --git a/task-launcher/cypress.config.js b/task-launcher/cypress.config.js index b44af7de7..fc2f0408c 100644 --- a/task-launcher/cypress.config.js +++ b/task-launcher/cypress.config.js @@ -16,6 +16,9 @@ export default defineConfig({ requestTimeout: 30000, responseTimeout: 30000, pageLoadTimeout: 60000, + // Memory management settings + experimentalMemoryManagement: true, + numTestsKeptInMemory: 1, // Reduce video frame rate for smaller files env: { videoFrameRate: 5 // Lower frame rate for more compact videos diff --git a/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_complete.cy.js new file mode 100644 index 000000000..7253615bc --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_complete.cy.js @@ -0,0 +1,53 @@ +describe('adult-reasoning Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=adult-reasoning'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_simple.cy.js new file mode 100644 index 000000000..261cfcb3d --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/adult-reasoning_simple.cy.js @@ -0,0 +1,113 @@ +describe('adult-reasoning Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'adult-reasoning'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/adult_reasoning_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/adult_reasoning_complete.cy.js new file mode 100644 index 000000000..5fedd8ea6 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/adult_reasoning_complete.cy.js @@ -0,0 +1,67 @@ +describe('Adult Reasoning Complete Run', () => { + it('runs complete adult-reasoning task with screenshots', () => { + cy.visit('http://localhost:8080/?task=adult-reasoning', { timeout: 60000 }); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + let counter = 0; + + // Initial screenshot + cy.screenshot(`${String(counter++).padStart(2, '0')}-start`); + cy.wait(2000); + + // Start task + cy.get('body').then($body => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-clicked-ok`); + } + }); + + // Main loop - run for reasonable time taking screenshots + for (let i = 0; i < 25; i++) { + cy.wait(6000); + cy.screenshot(`${String(counter++).padStart(2, '0')}-step-${i}`); + + // Check if completed + cy.get('body').then($body => { + const text = $body.text().toLowerCase(); + if (text.includes('thank you') || text.includes('complete') || text.includes('exit') || text.includes('finished')) { + cy.screenshot(`${String(counter++).padStart(2, '0')}-completion-detected`); + return; // Done + } + + // Adult-reasoning specific interactions + if ($body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then($buttons => { + const randomIndex = Math.floor(Math.random() * $buttons.length); + cy.wrap($buttons[randomIndex]).click({ force: true }); + cy.wait(1000); + }); + } else if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } else if ($body.find('button').length > 0) { + // Click any available button + cy.get('button').then($buttons => { + const visibleButtons = $buttons.filter(':visible'); + if (visibleButtons.length > 0) { + cy.wrap(visibleButtons[0]).click({ force: true }); + } + }); + } + }); + } + + cy.screenshot(`${String(counter++).padStart(2, '0')}-final`); + }); +}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e-screenshot-scripts/egma-math_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/egma-math_complete.cy.js new file mode 100644 index 000000000..b9d90bfdc --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/egma-math_complete.cy.js @@ -0,0 +1,100 @@ +describe('egma-math Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=egma-math'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Enhanced interaction strategy for EGMA math + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (math problems) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/egma-math_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/egma-math_simple.cy.js new file mode 100644 index 000000000..adb9f02b8 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/egma-math_simple.cy.js @@ -0,0 +1,113 @@ +describe('egma-math Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'egma-math'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/egma_math_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/egma_math_complete.cy.js new file mode 100644 index 000000000..021ae9a64 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/egma_math_complete.cy.js @@ -0,0 +1,68 @@ +const egma_math_url = 'http://localhost:8080/?task=egma-math'; + +describe('Egma-math Complete Run', () => { + let screenshotCounter = 1; + + function takeScreenshot(description) { + cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); + screenshotCounter++; + } + + it('runs complete egma-math with screenshots', () => { + // Visit with fullscreen mocking and extended timeout + cy.visit(egma_math_url, { + timeout: 60000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 8 minutes with screenshots every 10 seconds + const totalDuration = 8 * 60 * 1000; // 8 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) { + cy.wait(screenshotInterval); + takeScreenshot(`interval-${i.toString().padStart(2, '0')}`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then(($body) => { + // Click OK buttons if they exist + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Click Continue buttons if they exist + if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Click Next buttons if they exist + if ($body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Click Start buttons if they exist + if ($body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } + // Click any visible buttons as fallback + if ($body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + takeScreenshot('final-state'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/egma_math_smart_capture.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/egma_math_smart_capture.cy.js new file mode 100644 index 000000000..0519ecba6 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/egma_math_smart_capture.cy.js @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_complete.cy.js new file mode 100644 index 000000000..b7c4d21de --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_complete.cy.js @@ -0,0 +1,100 @@ +describe('hearts-and-flowers Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=hearts-and-flowers'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Enhanced interaction strategy for Levante framework tasks + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (task questions) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_simple.cy.js new file mode 100644 index 000000000..3d63873e9 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/hearts-and-flowers_simple.cy.js @@ -0,0 +1,51 @@ +describe('hearts-and-flowers Simple Capture', () => { + it('should capture 24 screenshots over 2 minutes', () => { + // Visit the task + cy.visit('http://localhost:8080?task=hearts-and-flowers'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 24 screenshots over 2 minutes (every 5 seconds) + for (let i = 1; i <= 24; i++) { + // Wait 5 seconds + cy.wait(5000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Hearts and flowers specific - left/right buttons + else if ($body.find('button').length >= 2) { + // Randomly click left or right button + const buttonIndex = Math.floor(Math.random() * 2); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e-screenshot-scripts/intro_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/intro_complete.cy.js new file mode 100644 index 000000000..04ff2b13a --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/intro_complete.cy.js @@ -0,0 +1,53 @@ +describe('intro Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=intro'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/intro_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/intro_simple.cy.js new file mode 100644 index 000000000..d89e85e27 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/intro_simple.cy.js @@ -0,0 +1,113 @@ +describe('intro Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'intro'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_complete.cy.js new file mode 100644 index 000000000..1ad34dc9b --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_complete.cy.js @@ -0,0 +1,114 @@ +import { testAfc } from './helpers.cy.js'; + +describe('Matrix Reasoning - Complete Screenshot Capture', () => { + it('captures complete matrix-reasoning task with automatic progression', () => { + let screenshotCounter = 0; + let maxSteps = 200; // Increased limit to capture complete task + let completionDetected = false; + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + + // Visit with fullscreen mocking using onBeforeLoad (key insight from working tests) + cy.visit('http://localhost:8080/?task=matrix-reasoning', { + timeout: 120000, + onBeforeLoad: (win) => { + // Enhanced fullscreen mocking before page loads + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + Object.defineProperty(win.screen, 'orientation', { + value: { lock: cy.stub().resolves() }, + writable: true + }); + + // Mock realClick for Cypress Real Events + win.Element.prototype.realClick = function() { + this.click(); + return Promise.resolve(); + }; + } + }); + + // Take initial screenshot + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-initial-load`); + cy.wait(3000); + + // Use the working testAfc pattern but with screenshot capture + cy.contains('OK', { timeout: 600000 }).should('be.visible'); + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-ok-button-found`); + + // Click OK with realClick simulation + cy.contains('OK').then(($btn) => { + $btn[0].click(); // Direct click to avoid fullscreen issues + }); + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-task-started`); + cy.wait(2000); + + // Main task loop with screenshot capture + function taskLoopWithScreenshots(stepNumber) { + if (stepNumber > maxSteps || completionDetected) { + console.log(`๐Ÿ Stopping: Step limit (${maxSteps}) reached or completion detected`); + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-final-state`); + return; + } + + console.log(`๐Ÿ“ธ Step ${stepNumber}/${maxSteps} - Screenshot ${screenshotCounter}`); + + // Wait for stimulus container + cy.get('.lev-stimulus-container', { timeout: 60000 }).should('exist').then(() => { + // Take screenshot of current state + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-step-${stepNumber}`); + cy.wait(2000); + + // Check for task completion + cy.get('body').then(($body) => { + if ($body.find('footer').length > 0) { + console.log('๐Ÿ Task completed - found footer'); + completionDetected = true; + cy.contains('Exit').click({ timeout: 60000 }); + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-task-completed`); + return; + } + + // Handle matrix reasoning interactions (based on original test pattern) + cy.get('.jspsych-content').then((content) => { + const responseButtons = content.find('.image'); // Matrix reasoning uses .image class + const correctButtons = content.find('.correct'); + const primaryButtons = content.find('.primary'); + + if (correctButtons.length > 0) { + console.log('๐ŸŽฏ Clicking correct answer'); + cy.get('.correct').click({ timeout: 60000 }); + } else if (responseButtons.length > 1) { + console.log('๐Ÿ–ผ๏ธ Clicking matrix image option'); + // Click a random image option for matrix reasoning + cy.get('.image').then(($images) => { + const randomIndex = Math.floor(Math.random() * $images.length); + cy.wrap($images.eq(randomIndex)).click(); + }); + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').click({ timeout: 60000 }); + } + }); + + cy.wait(3000); + + // Continue to next step after stimulus container disappears + cy.get('.lev-stimulus-container', { timeout: 60000 }).should('not.exist').then(() => { + if (!completionDetected) { + taskLoopWithScreenshots(stepNumber + 1); + } + }); + }); + }); + } + + // Start the enhanced task loop + taskLoopWithScreenshots(1); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_simple.cy.js new file mode 100644 index 000000000..c72ddbebc --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/matrix-reasoning_simple.cy.js @@ -0,0 +1,163 @@ +describe('matrix-reasoning Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + let stuckCounter = 0; + let maxStuckAttempts = 3; + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + + // Handle uncaught exceptions (especially fullscreen errors) + Cypress.on('uncaught:exception', (err, runnable) => { + // Don't fail the test on fullscreen permission errors + if (err.message.includes('Permissions check failed') || + err.message.includes('fullscreen') || + err.message.includes('requestFullscreen')) { + console.log('Ignoring fullscreen error:', err.message); + return false; + } + return true; + }); + + // Visit with comprehensive fullscreen mocking using onBeforeLoad + cy.visit('http://localhost:8080?task=matrix-reasoning', { + onBeforeLoad: (win) => { + // Comprehensive fullscreen API mocking + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + + // Mock all fullscreen properties + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + Object.defineProperty(win.document, 'webkitFullscreenEnabled', { + get: () => true, + configurable: true + }); + Object.defineProperty(win.document, 'mozFullScreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock screen orientation + Object.defineProperty(win.screen, 'orientation', { + value: { + lock: cy.stub().resolves(), + unlock: cy.stub().resolves(), + angle: 0, + type: 'landscape-primary' + }, + writable: true + }); + + // Mock permissions API + if (win.navigator.permissions) { + win.navigator.permissions.query = cy.stub().resolves({ state: 'granted' }); + } + } + }); + + // Take initial screenshot + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-start`); + + // Capture screenshots with enhanced stuck detection + for (let i = 1; i <= 100; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Check for completion first + cy.get('body').then(($body) => { + if ($body.find('footer').length > 0) { + console.log('๐Ÿ Task completed - found footer'); + cy.contains('Exit').click({ timeout: 60000 }); + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-completed`); + return; + } + + // Check if we're stuck (empty screen or no interactive elements) + const hasContent = $body.find('.lev-stimulus-container, .jspsych-content, button, .image, .primary').length > 0; + const hasInteractiveElements = $body.find('button:not([disabled]), .image, .primary, .correct, [data-choice]').length > 0; + + if (!hasContent || !hasInteractiveElements) { + stuckCounter++; + console.log(`โš ๏ธ Stuck detection: ${stuckCounter}/${maxStuckAttempts} - No content or interactive elements`); + + if (stuckCounter >= maxStuckAttempts) { + console.log('๐Ÿ”„ Attempting restart due to stuck state'); + // Try to restart by refreshing and continuing + cy.reload(); + cy.wait(3000); + stuckCounter = 0; // Reset stuck counter after reload + } else { + // Try some fallback interactions + cy.get('body').click('center'); + cy.wait(1000); + cy.get('body').type('{enter}'); + cy.wait(1000); + cy.get('body').type(' '); + } + } else { + stuckCounter = 0; // Reset stuck counter if we have content + + // Try interactions before taking screenshot + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + console.log(`Step ${i}: Clicking navigation button`); + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Matrix reasoning specific - correct answers + else if ($body.find('.correct').length > 0) { + console.log(`Step ${i}: Clicking correct answer`); + cy.get('.correct').first().click(); + } + // Strategy 3: Matrix reasoning specific - image options + else if ($body.find('.image').length > 1) { + console.log(`Step ${i}: Clicking matrix image option`); + cy.get('.image').then(($images) => { + const randomIndex = Math.floor(Math.random() * $images.length); + cy.wrap($images.eq(randomIndex)).click(); + }); + } + // Strategy 4: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + console.log(`Step ${i}: Clicking choice button`); + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 5: Primary buttons + else if ($body.find('.primary').length > 0) { + console.log(`Step ${i}: Clicking primary button`); + cy.get('.primary').first().click(); + } + // Strategy 6: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + console.log(`Step ${i}: Clicking any enabled button`); + cy.get('button:not([disabled])').first().click(); + } + // Strategy 7: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + console.log(`Step ${i}: Random button selection`); + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 8: Fallback interactions + else { + console.log(`Step ${i}: Fallback interaction`); + cy.get('body').click('center'); + cy.wait(500); + cy.get('body').type('{space}'); + } + } + + // Take screenshot + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-step-${i}`); + }); + } + + // Final screenshot + cy.screenshot(`${timestamp}-matrix-reasoning-${(++screenshotCounter).toString().padStart(3, '0')}-final`); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/matrix_reasoning_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/matrix_reasoning_complete.cy.js new file mode 100644 index 000000000..7914ab305 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/matrix_reasoning_complete.cy.js @@ -0,0 +1,65 @@ +const matrix_reasoning_url = 'http://localhost:8080/?task=matrix-reasoning'; + +describe('Matrix Reasoning Complete Run', () => { + it('runs complete matrix-reasoning task with screenshots', () => { + cy.visit('http://localhost:8080/?task=matrix-reasoning', { timeout: 60000 }); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + let counter = 0; + + // Initial screenshot + cy.screenshot(`${String(counter++).padStart(2, '0')}-start`); + cy.wait(2000); + + // Start task + cy.get('body').then($body => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-clicked-ok`); + } + }); + + // Main loop - run for reasonable time taking screenshots + for (let i = 0; i < 20; i++) { + cy.wait(8000); + cy.screenshot(`${String(counter++).padStart(2, '0')}-step-${i}`); + + // Check if completed + cy.get('body').then($body => { + const text = $body.text().toLowerCase(); + if (text.includes('thank you') || text.includes('complete') || text.includes('exit') || text.includes('finished')) { + cy.screenshot(`${String(counter++).padStart(2, '0')}-completion-detected`); + return; // Done + } + + // Matrix reasoning interactions - click on answer options + if ($body.find('button').length > 0) { + cy.get('button').then($buttons => { + const visibleButtons = $buttons.filter(':visible'); + if (visibleButtons.length > 0) { + // Try to find answer buttons first + const answerButtons = visibleButtons.filter(':contains("A"), :contains("B"), :contains("C"), :contains("D")'); + if (answerButtons.length > 0) { + const randomIndex = Math.floor(Math.random() * answerButtons.length); + cy.wrap(answerButtons[randomIndex]).click({ force: true }); + } else { + // Click any visible button + const randomIndex = Math.floor(Math.random() * visibleButtons.length); + cy.wrap(visibleButtons[randomIndex]).click({ force: true }); + } + } + }); + } + }); + } + + cy.screenshot(`${String(counter++).padStart(2, '0')}-final`); + }); +}); diff --git a/task-launcher/cypress/e2e/memory_simple_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/memory_simple_complete.cy.js similarity index 100% rename from task-launcher/cypress/e2e/memory_simple_complete.cy.js rename to task-launcher/cypress/e2e-screenshot-scripts/memory_simple_complete.cy.js diff --git a/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_complete.cy.js new file mode 100644 index 000000000..c217d4bb3 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_complete.cy.js @@ -0,0 +1,100 @@ +describe('mental-rotation Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=mental-rotation'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Enhanced interaction strategy for Levante framework tasks + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (task questions) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_simple.cy.js new file mode 100644 index 000000000..35d5369a5 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/mental-rotation_simple.cy.js @@ -0,0 +1,113 @@ +describe('mental-rotation Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'mental-rotation'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/mental_rotation_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/mental_rotation_complete.cy.js new file mode 100644 index 000000000..78d857059 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/mental_rotation_complete.cy.js @@ -0,0 +1,68 @@ +const mental_rotation_url = 'http://localhost:8080/?task=mental-rotation'; + +describe('Mental-rotation Complete Run', () => { + let screenshotCounter = 1; + + function takeScreenshot(description) { + cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); + screenshotCounter++; + } + + it('runs complete mental-rotation with screenshots', () => { + // Visit with fullscreen mocking and extended timeout + cy.visit(mental_rotation_url, { + timeout: 60000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 3 minutes with screenshots every 10 seconds + const totalDuration = 3 * 60 * 1000; // 3 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) { + cy.wait(screenshotInterval); + takeScreenshot(`interval-${i.toString().padStart(2, '0')}`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then(($body) => { + // Click OK buttons if they exist + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Click Continue buttons if they exist + if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Click Next buttons if they exist + if ($body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Click Start buttons if they exist + if ($body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } + // Click any visible buttons as fallback + if ($body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + takeScreenshot('final-state'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_complete.cy.js new file mode 100644 index 000000000..817a1698f --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_complete.cy.js @@ -0,0 +1,53 @@ +describe('roar-inference Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=roar-inference'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_simple.cy.js new file mode 100644 index 000000000..1879c4572 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/roar-inference_simple.cy.js @@ -0,0 +1,113 @@ +describe('roar-inference Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'roar-inference'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_complete.cy.js new file mode 100644 index 000000000..cf2deeefc --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_complete.cy.js @@ -0,0 +1,100 @@ +describe('same-different-selection Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=same-different-selection'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Enhanced interaction strategy for Levante framework tasks + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (task questions) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_simple.cy.js new file mode 100644 index 000000000..ab4718a49 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/same-different-selection_simple.cy.js @@ -0,0 +1,113 @@ +describe('same-different-selection Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'same-different-selection'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/theory-of-mind_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/theory-of-mind_complete.cy.js new file mode 100644 index 000000000..47838adcf --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/theory-of-mind_complete.cy.js @@ -0,0 +1,53 @@ +describe('theory-of-mind Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=theory-of-mind'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/trog_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/trog_complete.cy.js new file mode 100644 index 000000000..74f8f55b5 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/trog_complete.cy.js @@ -0,0 +1,53 @@ +describe('trog Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=trog'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Try interactions before taking screenshot + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click(); + } + // Strategy 2: Task-specific elements + else if ($body.find('[data-choice], .choice-button, .response-button').length > 0) { + cy.get('[data-choice], .choice-button, .response-button').first().click(); + } + // Strategy 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + cy.get('button:not([disabled])').first().click(); + } + // Strategy 4: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/trog_simple.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/trog_simple.cy.js new file mode 100644 index 000000000..09387dd19 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/trog_simple.cy.js @@ -0,0 +1,113 @@ +describe('trog Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = 'trog'; + const screenshotInterval = 6 * 1000; + const maxDuration = 240 * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then($body => { + // Priority 1: Continue/OK/Next buttons + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if ($body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = $body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if ($body.find('button:not([disabled])').length > 0) { + const buttons = $body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if ($body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); diff --git a/task-launcher/cypress/e2e-screenshot-scripts/vocab_complete.cy.js b/task-launcher/cypress/e2e-screenshot-scripts/vocab_complete.cy.js new file mode 100644 index 000000000..9b95066c1 --- /dev/null +++ b/task-launcher/cypress/e2e-screenshot-scripts/vocab_complete.cy.js @@ -0,0 +1,100 @@ +describe('vocab Complete Task Capture', () => { + it('should capture screenshots throughout entire task', () => { + let screenshotCounter = 0; + + // Visit the task + cy.visit('http://localhost:8080?task=vocab'); + + // Mock fullscreen API to prevent errors + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Capture 48 screenshots over 240 seconds (every 5 seconds) + for (let i = 1; i <= 48; i++) { + // Wait 5 seconds + cy.wait(5 * 1000); + + // Enhanced interaction strategy for Levante framework tasks + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (task questions) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); diff --git a/task-launcher/cypress/e2e/memory_bypass_fullscreen.cy.js b/task-launcher/cypress/e2e/memory_bypass_fullscreen.cy.js deleted file mode 100644 index 0cdc45dc8..000000000 --- a/task-launcher/cypress/e2e/memory_bypass_fullscreen.cy.js +++ /dev/null @@ -1,72 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Bypass Fullscreen', () => { - let screenshotCounter = 1; - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - cy.wait(1000); - } - - it('bypasses fullscreen permission and captures memory game', () => { - // Visit the page and immediately mock fullscreen API - cy.visit(memory_game_url, { - onBeforeLoad: (win) => { - // Mock the fullscreen API to always succeed - win.document.documentElement.requestFullscreen = cy.stub().resolves(); - win.document.exitFullscreen = cy.stub().resolves(); - - // Mock fullscreen properties - Object.defineProperty(win.document, 'fullscreenElement', { - get: () => win.document.documentElement, - configurable: true - }); - - Object.defineProperty(win.document, 'fullscreenEnabled', { - get: () => true, - configurable: true - }); - - // Mock permissions API to always allow fullscreen - if (win.navigator.permissions) { - win.navigator.permissions.query = cy.stub().resolves({ state: 'granted' }); - } - } - }); - - takeScreenshot('01-initial-load-with-mocked-fullscreen'); - - // Wait for page to load - cy.wait(3000); - takeScreenshot('02-page-loaded'); - - // Now click OK button since fullscreen should be mocked - cy.contains('OK', { timeout: 10000 }).should('be.visible'); - takeScreenshot('03-ok-button-visible'); - - cy.contains('OK').click({ force: true }); - takeScreenshot('04-after-ok-click'); - - // Wait for memory game to initialize - cy.wait(5000); - takeScreenshot('05-after-fullscreen-attempt'); - - // Take screenshots at intervals to capture game progression - for (let i = 0; i < 10; i++) { - cy.wait(3000); - takeScreenshot(`06-game-state-${i + 1}`); - - // Try to interact with any visible game elements - cy.get('body').then(($body) => { - const clickableElements = $body.find('button:visible, [onclick]:visible, canvas:visible'); - if (clickableElements.length > 0) { - cy.wrap(clickableElements.first()).click({ force: true }); - takeScreenshot(`07-interaction-${i + 1}`); - } - }); - } - - takeScreenshot('08-final-state'); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_complete_run.cy.js b/task-launcher/cypress/e2e/memory_complete_run.cy.js deleted file mode 100644 index a1363fcb3..000000000 --- a/task-launcher/cypress/e2e/memory_complete_run.cy.js +++ /dev/null @@ -1,119 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Complete Run', () => { - let screenshotCounter = 1; - let gameLoopCount = 0; - const maxGameLoops = 20; // Prevent infinite loops - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('completes full memory game with strategic screenshots', () => { - // Visit with fullscreen mocking and extended timeout - cy.visit(memory_game_url, { - timeout: 60000, - onBeforeLoad: (win) => { - win.document.documentElement.requestFullscreen = cy.stub().resolves(); - win.document.exitFullscreen = cy.stub().resolves(); - Object.defineProperty(win.document, 'fullscreenElement', { - get: () => win.document.documentElement, - configurable: true - }); - Object.defineProperty(win.document, 'fullscreenEnabled', { - get: () => true, - configurable: true - }); - } - }); - - takeScreenshot('01-initial-load'); - - // Start the game - cy.contains('OK', { timeout: 60000 }).should('be.visible'); - cy.contains('OK').click({ force: true }); - takeScreenshot('02-game-started'); - - // Run the complete memory game - playCompleteMemoryGame(); - }); - - function playCompleteMemoryGame() { - gameLoopCount++; - - if (gameLoopCount > maxGameLoops) { - takeScreenshot('99-max-loops-reached'); - return; - } - - cy.get('body', { timeout: 30000 }).then(($body) => { - // Check for end screen - if ($body.find('p,h1').text().includes('Thank you!') || - $body.find('p,h1').text().includes('complete') || - $body.find('p,h1').text().includes('finished')) { - takeScreenshot('98-game-complete'); - return; - } - - // Look for jspsych content - cy.get('.jspsych-content', { timeout: 15000 }).then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - // Instructions screen - take screenshot every few instructions - if (gameLoopCount % 3 === 0) { - takeScreenshot(`03-instructions-${Math.floor(gameLoopCount / 3)}`); - } - - // Click OK or continue buttons - cy.get('body').then(($body) => { - const okButton = $body.find('button:contains("OK")'); - const continueButton = $body.find('button:contains("Continue"), button:contains("Next")'); - - if (okButton.length > 0) { - cy.wrap(okButton.first()).click({ force: true }); - } else if (continueButton.length > 0) { - cy.wrap(continueButton.first()).click({ force: true }); - } - - cy.wait(2000); - playCompleteMemoryGame(); - }); - - } else { - // Actual memory game trial - takeScreenshot(`04-memory-trial-${Math.floor(gameLoopCount / 5)}`); - - // Play the memory trial - playMemoryTrial(corsiBlocks); - } - }); - }); - } - - function playMemoryTrial(blocks) { - // Wait for sequence display to end - cy.get('p', { timeout: 30000 }).should('not.exist'); - - // Wait for response phase - cy.get('p', { timeout: 15000 }).should('exist'); - - // Take screenshot of response phase - takeScreenshot(`05-response-phase-${Math.floor(gameLoopCount / 5)}`); - - // Click blocks randomly (since we don't have correct sequence) - const numClicks = Math.min(3, blocks.length); - for (let i = 0; i < numClicks; i++) { - const randomIndex = Math.floor(Math.random() * blocks.length); - cy.wrap(blocks[randomIndex]).click({ force: true }); - cy.wait(500); - } - - // Wait for trial to process - cy.wait(3000); - - // Continue the game - playCompleteMemoryGame(); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_complete_run_fixed.cy.js b/task-launcher/cypress/e2e/memory_complete_run_fixed.cy.js deleted file mode 100644 index a8d22540e..000000000 --- a/task-launcher/cypress/e2e/memory_complete_run_fixed.cy.js +++ /dev/null @@ -1,146 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Complete Run Fixed', () => { - let screenshotCounter = 1; - let gameLoopCount = 0; - const maxGameLoops = 50; // Increased to allow more trials - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('completes full memory game with fixed DOM handling', () => { - // Visit with fullscreen mocking and extended timeout - cy.visit(memory_game_url, { - timeout: 60000, - onBeforeLoad: (win) => { - win.document.documentElement.requestFullscreen = cy.stub().resolves(); - win.document.exitFullscreen = cy.stub().resolves(); - Object.defineProperty(win.document, 'fullscreenElement', { - get: () => win.document.documentElement, - configurable: true - }); - Object.defineProperty(win.document, 'fullscreenEnabled', { - get: () => true, - configurable: true - }); - } - }); - - takeScreenshot('01-initial-load'); - - // Start the game - cy.contains('OK', { timeout: 60000 }).should('be.visible'); - cy.contains('OK').click({ force: true }); - takeScreenshot('02-game-started'); - - // Run the complete memory game - playCompleteMemoryGame(); - }); - - function playCompleteMemoryGame() { - gameLoopCount++; - - if (gameLoopCount > maxGameLoops) { - takeScreenshot('99-max-loops-reached'); - return; - } - - cy.get('body', { timeout: 30000 }).then(($body) => { - // Check for end screen - const bodyText = $body.text(); - if (bodyText.includes('Thank you!') || - bodyText.includes('complete') || - bodyText.includes('finished') || - bodyText.includes('Exit')) { - takeScreenshot('98-game-complete'); - - // Click Exit if available - if ($body.find('button:contains("Exit")').length > 0) { - cy.contains('Exit').click({ force: true }); - takeScreenshot('99-clicked-exit'); - } - return; - } - - // Check if jspsych content exists - cy.get('body').then(() => { - cy.get('.jspsych-content', { timeout: 10000 }) - .should('exist') - .then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - // Instructions screen - take screenshot every few instructions - if (gameLoopCount % 5 === 0) { - takeScreenshot(`03-instructions-${Math.floor(gameLoopCount / 5)}`); - } - - // Click OK or continue buttons - cy.get('body').then(($body) => { - const okButton = $body.find('button:contains("OK")'); - const continueButton = $body.find('button:contains("Continue"), button:contains("Next")'); - - if (okButton.length > 0) { - cy.contains('OK').click({ force: true }); - } else if (continueButton.length > 0) { - cy.get('button').contains(/Continue|Next/).first().click({ force: true }); - } - - cy.wait(3000); - playCompleteMemoryGame(); - }); - - } else { - // Actual memory game trial - if (gameLoopCount % 3 === 0) { - takeScreenshot(`04-memory-trial-${Math.floor(gameLoopCount / 3)}`); - } - - // Play the memory trial with fixed DOM handling - playMemoryTrialFixed(); - } - }); - }); - }); - } - - function playMemoryTrialFixed() { - // Wait for sequence display to end (when p element disappears) - cy.get('body').then(($body) => { - if ($body.find('p').length > 0) { - cy.get('p', { timeout: 30000 }).should('not.exist'); - } - }); - - // Wait for response phase (when p element appears again) - cy.get('p', { timeout: 15000 }).should('exist'); - - // Take screenshot of response phase - if (gameLoopCount % 3 === 0) { - takeScreenshot(`05-response-phase-${Math.floor(gameLoopCount / 3)}`); - } - - // Check if blocks exist and click them - cy.get('body').then(($body) => { - const blocks = $body.find('.jspsych-corsi-block'); - - if (blocks.length > 0) { - // Click first few blocks - const numClicks = Math.min(2, blocks.length); - - for (let i = 0; i < numClicks; i++) { - cy.get('.jspsych-corsi-block').eq(i).click({ force: true }); - cy.wait(800); - } - } - - // Wait for trial to process - cy.wait(4000); - - // Continue the game - playCompleteMemoryGame(); - }); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_comprehensive_screenshots.cy.js b/task-launcher/cypress/e2e/memory_comprehensive_screenshots.cy.js deleted file mode 100644 index 83ee8fccc..000000000 --- a/task-launcher/cypress/e2e/memory_comprehensive_screenshots.cy.js +++ /dev/null @@ -1,71 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Comprehensive Screenshots', () => { - let screenshotCounter = 1; - - // Handle any uncaught exceptions gracefully - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - // Don't fail the test on uncaught exceptions - return false; - }); - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - // Small wait between screenshots to capture state changes - cy.wait(500); - } - - it('captures comprehensive screenshots of the memory game', () => { - cy.visit(memory_game_url); - takeScreenshot('01-initial-load'); - - // Wait for page to fully load - cy.wait(3000); - takeScreenshot('02-after-page-load'); - - // Look for and interact with common UI elements - cy.get('body').then(($body) => { - // Take screenshot of whatever is currently visible - takeScreenshot('03-body-loaded'); - - // Look for buttons and take screenshots - if ($body.find('button').length > 0) { - takeScreenshot('04-buttons-found'); - - // Try to click any visible buttons - cy.get('button:visible').first().then(($btn) => { - takeScreenshot('05-before-button-click'); - cy.wrap($btn).click({ force: true }); - takeScreenshot('06-after-button-click'); - }); - } - - // Wait and take more screenshots to capture any animations or state changes - cy.wait(2000); - takeScreenshot('07-after-wait-2s'); - - cy.wait(3000); - takeScreenshot('08-after-wait-5s'); - - // Look for any game elements that might have appeared - if ($body.find('.memory-card, .card, .game-board, [class*="memory"], [class*="card"]').length > 0) { - takeScreenshot('09-game-elements-found'); - } - - // Try to interact with any clickable elements - cy.get('body').find('*').each(($el) => { - if ($el.is(':visible') && ($el.is('button') || $el.is('[onclick]') || $el.css('cursor') === 'pointer')) { - cy.wrap($el).click({ force: true }); - takeScreenshot(`10-clicked-element-${$el.prop('tagName')}`); - cy.wait(1000); - } - }); - - // Take final screenshots - cy.wait(5000); - takeScreenshot('11-final-state'); - }); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_direct_screenshots.cy.js b/task-launcher/cypress/e2e/memory_direct_screenshots.cy.js deleted file mode 100644 index 6a2a5426c..000000000 --- a/task-launcher/cypress/e2e/memory_direct_screenshots.cy.js +++ /dev/null @@ -1,38 +0,0 @@ -const memory_game_url = 'http://localhost:8082/?task=memory-game'; - -describe('Memory Game Direct Screenshots', () => { - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - return false; - }); - - it('takes direct screenshots of memory game at intervals', () => { - cy.visit(memory_game_url); - - // Wait for the page to load completely - cy.get('body').should('be.visible'); - - // Take initial screenshot - cy.screenshot('01-initial-load'); - cy.wait(2000); - - // Look for and click OK button if it exists - cy.get('body').then(($body) => { - if ($body.find('button:contains("OK")').length > 0) { - cy.contains('OK').click({ force: true }); - cy.screenshot('02-after-ok-click'); - cy.wait(2000); - } - }); - - // Take screenshots every 3 seconds for the next 30 seconds - for (let i = 3; i <= 12; i++) { - cy.wait(3000); - cy.screenshot(`${i.toString().padStart(2, '0')}-game-progress`); - } - - // Take a final screenshot - cy.wait(5000); - cy.screenshot('13-final-state'); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_final_screenshots.cy.js b/task-launcher/cypress/e2e/memory_final_screenshots.cy.js deleted file mode 100644 index bc70870f2..000000000 --- a/task-launcher/cypress/e2e/memory_final_screenshots.cy.js +++ /dev/null @@ -1,138 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Final Screenshots', () => { - let screenshotCounter = 1; - - // Handle fullscreen permission errors - Cypress.on('uncaught:exception', (err, runnable) => { - // Ignore fullscreen permission errors - if (err.message.includes('Permissions check failed') || - err.message.includes('fullscreen') || - err.message.includes('enterFullScreen')) { - return false; - } - // Let other errors fail the test - return true; - }); - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('captures memory game screenshots with error handling', () => { - cy.visit(memory_game_url); - takeScreenshot('initial-load'); - - // wait for OK button to appear - cy.contains('OK', { timeout: 300000 }).should('be.visible'); - takeScreenshot('ok-button-visible'); - - // Try to click OK, but handle fullscreen errors gracefully - cy.contains('OK').click({ force: true }); - takeScreenshot('after-ok-click'); - - // Wait a bit for any transitions - cy.wait(3000); - takeScreenshot('after-wait'); - - cy.get('p').then(() => { - memoryLoop(); - }); - - // Try to find exit button, but don't fail if it's not there - cy.get('body').then(($body) => { - if ($body.find('button:contains("Exit")').length > 0) { - takeScreenshot('before-exit'); - cy.contains('Exit').click(); - takeScreenshot('final-exit'); - } else { - takeScreenshot('no-exit-button-found'); - } - }); - }); - - function handleInstructions() { - takeScreenshot('instructions-screen'); - - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - // Look for any button that might advance the instructions - cy.get('body').then(($body) => { - if ($body.find('button:contains("OK")').length > 0) { - cy.contains('OK').click(); - takeScreenshot('after-instructions-ok'); - } else if ($body.find('button').length > 0) { - cy.get('button').first().click(); - takeScreenshot('after-instructions-button'); - } - }); - } - }); - return; - } - - function answerTrial() { - takeScreenshot('trial-start'); - - // wait for gap after display phase - cy.get('p', { timeout: 20000 }).should('not.exist'); - cy.get('p').should('exist'); - - takeScreenshot('after-sequence-display'); - - cy.get('.jspsych-content').then((content) => { - const blocks = content.find('.jspsych-corsi-block'); - - if (blocks.length > 0) { - takeScreenshot('blocks-visible-for-input'); - - // wait for window to contain sequence information - cy.window().its('cypressData').should('have.property', 'correctAnswer'); - - cy.window().then((window) => { - const sequence = window.cypressData.correctAnswer; - - sequence.forEach((number, index) => { - blocks[number].click(); - takeScreenshot(`clicking-block-${index + 1}-of-${sequence.length}`); - cy.wait(500); // Small delay between clicks - }); - - cy.get('p').should('not.exist', { timeout: 5000 }); - takeScreenshot('trial-completed'); - }); - } - }); - return; - } - - function memoryLoop() { - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length > 0) { - answerTrial(); - } else { - handleInstructions(); - } - }); - - // end recursion if the task has reached the end screen - cy.get('p,h1').then((p) => { - if (p[0].textContent.includes('Thank you!')) { - takeScreenshot('thank-you-screen'); - return; - } else { - // Limit the number of loops to prevent infinite recursion - if (screenshotCounter < 50) { - memoryLoop(); - } else { - takeScreenshot('max-screenshots-reached'); - } - } - }); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_headed_test.cy.js b/task-launcher/cypress/e2e/memory_headed_test.cy.js deleted file mode 100644 index 737630119..000000000 --- a/task-launcher/cypress/e2e/memory_headed_test.cy.js +++ /dev/null @@ -1,23 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Headed Test', () => { - it('opens memory game in headed mode to see actual content', () => { - cy.visit(memory_game_url); - - // Take a screenshot immediately - cy.screenshot('headed-initial'); - - // Wait and take another screenshot - cy.wait(5000); - cy.screenshot('headed-after-wait'); - - // Look for any visible elements - cy.get('body').then(($body) => { - cy.log('Body HTML length:', $body.html().length); - cy.log('Visible buttons:', $body.find('button:visible').length); - cy.log('Visible divs:', $body.find('div:visible').length); - }); - - cy.screenshot('headed-final'); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_no_fullscreen.cy.js b/task-launcher/cypress/e2e/memory_no_fullscreen.cy.js deleted file mode 100644 index f8f7f5346..000000000 --- a/task-launcher/cypress/e2e/memory_no_fullscreen.cy.js +++ /dev/null @@ -1,94 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Without Fullscreen', () => { - let screenshotCounter = 1; - - // Handle any uncaught exceptions gracefully - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - // Don't fail the test on uncaught exceptions - return false; - }); - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - cy.wait(1000); // Wait between screenshots - } - - it('captures memory game without fullscreen mode', () => { - cy.visit(memory_game_url); - takeScreenshot('01-initial-load'); - - // Wait for page to fully load - cy.wait(5000); - takeScreenshot('02-page-loaded'); - - // Look for OK button but DON'T click it (to avoid fullscreen) - cy.get('body').then(($body) => { - takeScreenshot('03-looking-for-elements'); - - // Try to find the memory game without going fullscreen - // Look for any game-related elements - const gameSelectors = [ - '[class*="memory"]', - '[class*="card"]', - '[class*="game"]', - '[id*="memory"]', - '[id*="game"]', - 'canvas', - 'svg' - ]; - - gameSelectors.forEach((selector, index) => { - cy.get('body').then(($body) => { - if ($body.find(selector).length > 0) { - takeScreenshot(`04-found-${selector.replace(/[^a-zA-Z0-9]/g, '')}-elements`); - } - }); - }); - - // Try to bypass fullscreen by directly manipulating the page - cy.window().then((win) => { - // Try to find and trigger the memory game without fullscreen - if (win.document.querySelector('button')) { - takeScreenshot('05-buttons-available'); - - // Look for non-fullscreen buttons - cy.get('button').each(($btn) => { - const btnText = $btn.text().toLowerCase(); - if (!btnText.includes('ok') && !btnText.includes('fullscreen')) { - cy.wrap($btn).click({ force: true }); - takeScreenshot(`06-clicked-${btnText.replace(/[^a-zA-Z0-9]/g, '')}`); - cy.wait(2000); - } - }); - } - - // Try to trigger memory game initialization directly - cy.window().its('jsPsych').then((jsPsych) => { - if (jsPsych) { - takeScreenshot('07-jspsych-available'); - // Try to access the memory game without fullscreen - } - }).then(() => { - takeScreenshot('08-after-jspsych-check'); - }, () => { - takeScreenshot('08-no-jspsych'); - }); - - // Wait and take more screenshots to see if anything loads - cy.wait(5000); - takeScreenshot('09-after-long-wait'); - - // Try to interact with any visible elements - cy.get('body *:visible').then(($elements) => { - takeScreenshot(`10-visible-elements-count-${$elements.length}`); - }); - - cy.wait(3000); - takeScreenshot('11-final-state'); - }); - }); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_passive_capture.cy.js b/task-launcher/cypress/e2e/memory_passive_capture.cy.js deleted file mode 100644 index 1f91ce602..000000000 --- a/task-launcher/cypress/e2e/memory_passive_capture.cy.js +++ /dev/null @@ -1,24 +0,0 @@ -const memory_game_url = 'http://localhost:8082/?task=memory-game'; - -describe('Memory Game Passive Video Capture', () => { - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - return false; - }); - - it('passively records memory game without any interactions', () => { - cy.visit(memory_game_url); - - // Wait for the page to load completely - cy.get('body').should('be.visible'); - - // Wait a bit for any initial loading - cy.wait(3000); - - // Just let the game run for 60 seconds without any interactions - // This should capture whatever the game shows naturally - cy.wait(60000); - - // The test ends here - we've captured 60 seconds of whatever the game displays - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_production_capture.cy.js b/task-launcher/cypress/e2e/memory_production_capture.cy.js deleted file mode 100644 index b8c5cf948..000000000 --- a/task-launcher/cypress/e2e/memory_production_capture.cy.js +++ /dev/null @@ -1,64 +0,0 @@ -const memory_game_url = 'http://localhost:8082/?task=memory-game'; - -describe('Memory Game Production Video Capture', () => { - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - return false; - }); - - it('records clean video of memory game without webpack overlays', () => { - cy.visit(memory_game_url); - - // Wait for the page to load completely - cy.get('body').should('be.visible'); - - // Wait for OK button to appear - cy.contains('OK', { timeout: 30000 }).should('be.visible'); - cy.contains('OK').click({ force: true }); - - // Wait for game to initialize - cy.wait(5000); - - // Play a simplified version of the memory game - for (let round = 0; round < 3; round++) { - // Wait for any sequence to play - cy.wait(3000); - - // Try to interact with the game - cy.get('body').then(($body) => { - const blocks = $body.find('.jspsych-corsi-block'); - if (blocks.length > 0) { - // Click a few blocks - cy.wrap(blocks[0]).click({ force: true }); - cy.wait(500); - if (blocks.length > 1) { - cy.wrap(blocks[1]).click({ force: true }); - } - } - - // Try to find and click OK or Submit buttons - const okBtn = $body.find('button:contains("OK")'); - const submitBtn = $body.find('button:contains("Submit")'); - - if (submitBtn.length > 0) { - cy.wrap(submitBtn.first()).click({ force: true }); - } else if (okBtn.length > 0) { - cy.wrap(okBtn.first()).click({ force: true }); - } - }); - - cy.wait(2000); - } - - // Let the game run a bit longer to capture more content - cy.wait(10000); - - // Try to exit gracefully - cy.get('body').then(($body) => { - const exitBtn = $body.find('button:contains("Exit")'); - if (exitBtn.length > 0) { - cy.wrap(exitBtn.first()).click({ force: true }); - } - }); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_proper_gameplay.cy.js b/task-launcher/cypress/e2e/memory_proper_gameplay.cy.js deleted file mode 100644 index e1990e6f4..000000000 --- a/task-launcher/cypress/e2e/memory_proper_gameplay.cy.js +++ /dev/null @@ -1,115 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Proper Gameplay', () => { - let screenshotCounter = 1; - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('captures memory game with proper gameplay interactions', () => { - // Visit with fullscreen mocking - cy.visit(memory_game_url, { - onBeforeLoad: (win) => { - win.document.documentElement.requestFullscreen = cy.stub().resolves(); - win.document.exitFullscreen = cy.stub().resolves(); - Object.defineProperty(win.document, 'fullscreenElement', { - get: () => win.document.documentElement, - configurable: true - }); - Object.defineProperty(win.document, 'fullscreenEnabled', { - get: () => true, - configurable: true - }); - } - }); - - takeScreenshot('01-initial-load'); - - // Wait for OK button and start fullscreen - cy.contains('OK', { timeout: 30000 }).should('be.visible'); - takeScreenshot('02-ok-button-visible'); - - cy.contains('OK').click({ force: true }); - takeScreenshot('03-after-fullscreen-start'); - - // Now follow the memory game pattern - playMemoryGame(); - }); - - function playMemoryGame() { - // Handle instructions and game screens - cy.get('.jspsych-content', { timeout: 10000 }).then((content) => { - takeScreenshot('04-jspsych-content-loaded'); - - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - // This is an instructions screen - takeScreenshot('05-instructions-screen'); - - // Look for OK button to continue - cy.get('body').then(($body) => { - if ($body.find('button:contains("OK")').length > 0) { - cy.contains('OK').click({ force: true }); - takeScreenshot('06-clicked-ok-in-instructions'); - cy.wait(3000); - - // Recursively continue the game - playMemoryGame(); - } else { - // Try other common continue buttons - const continueButtons = $body.find('button:contains("Continue"), button:contains("Next"), button:contains("Start")'); - if (continueButtons.length > 0) { - cy.wrap(continueButtons.first()).click({ force: true }); - takeScreenshot('07-clicked-continue-button'); - cy.wait(3000); - playMemoryGame(); - } else { - takeScreenshot('08-no-continue-buttons-found'); - cy.wait(5000); - playMemoryGame(); - } - } - }); - } else { - // This is the actual memory game with Corsi blocks - takeScreenshot('09-corsi-blocks-found'); - - // Wait for sequence display to finish - cy.wait(5000); - takeScreenshot('10-after-sequence-display'); - - // Try to play the memory game - playMemoryTrial(corsiBlocks); - } - }); - } - - function playMemoryTrial(blocks) { - takeScreenshot('11-starting-memory-trial'); - - // Wait for the display phase to end (when 'p' element disappears) - cy.get('p', { timeout: 20000 }).should('not.exist'); - takeScreenshot('12-display-phase-ended'); - - // Wait for response phase (when 'p' element reappears) - cy.get('p', { timeout: 10000 }).should('exist'); - takeScreenshot('13-response-phase-started'); - - // Click on some blocks (since we don't have the correct sequence) - // Just click a few blocks to see what happens - for (let i = 0; i < Math.min(3, blocks.length); i++) { - cy.wrap(blocks[i]).click({ force: true }); - takeScreenshot(`14-clicked-block-${i + 1}`); - cy.wait(1000); - } - - takeScreenshot('15-after-clicking-blocks'); - cy.wait(5000); - - // Continue the game loop - playMemoryGame(); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_slow_progression.cy.js b/task-launcher/cypress/e2e/memory_slow_progression.cy.js deleted file mode 100644 index ba0467254..000000000 --- a/task-launcher/cypress/e2e/memory_slow_progression.cy.js +++ /dev/null @@ -1,97 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game Slow Progression', () => { - let screenshotCounter = 1; - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('captures memory game with proper progression timing', () => { - // Visit with fullscreen mocking - cy.visit(memory_game_url, { - onBeforeLoad: (win) => { - // Mock fullscreen API - win.document.documentElement.requestFullscreen = cy.stub().resolves(); - win.document.exitFullscreen = cy.stub().resolves(); - Object.defineProperty(win.document, 'fullscreenElement', { - get: () => win.document.documentElement, - configurable: true - }); - Object.defineProperty(win.document, 'fullscreenEnabled', { - get: () => true, - configurable: true - }); - } - }); - - takeScreenshot('01-initial-load'); - - // Wait for page to load - cy.wait(5000); - takeScreenshot('02-page-loaded'); - - // Click OK button to start - cy.contains('OK', { timeout: 10000 }).should('be.visible'); - takeScreenshot('03-ok-button-visible'); - - cy.contains('OK').click({ force: true }); - takeScreenshot('04-after-ok-click'); - - // Wait longer for game to initialize - cy.wait(10000); - takeScreenshot('05-after-long-wait'); - - // Look for and interact with any "Continue" or "Next" buttons - cy.get('body').then(($body) => { - const continueButtons = $body.find('button:contains("Continue"), button:contains("Next"), button:contains("Start"), button:contains("Begin")'); - if (continueButtons.length > 0) { - cy.wrap(continueButtons.first()).click({ force: true }); - takeScreenshot('06-clicked-continue'); - cy.wait(5000); - } - }); - - // Try pressing common keys that might advance the game - cy.get('body').type(' '); // Spacebar - takeScreenshot('07-after-spacebar'); - cy.wait(3000); - - cy.get('body').type('{enter}'); // Enter key - takeScreenshot('08-after-enter'); - cy.wait(3000); - - // Look for any clickable game elements and interact with them - cy.get('body').then(($body) => { - // Look for memory cards, game buttons, or interactive elements - const gameElements = $body.find('canvas, svg, [class*="card"], [class*="memory"], [class*="game"], button:visible'); - - if (gameElements.length > 0) { - takeScreenshot('09-found-game-elements'); - - // Click on different elements to try to progress the game - gameElements.each((index, element) => { - if (index < 3) { // Only click first 3 elements - cy.wrap(element).click({ force: true }); - cy.wait(5000); // Wait 5 seconds between clicks - takeScreenshot(`10-clicked-element-${index + 1}`); - } - }); - } - }); - - // Wait much longer periods and take screenshots to see if game auto-progresses - for (let i = 0; i < 5; i++) { - cy.wait(15000); // Wait 15 seconds between screenshots - takeScreenshot(`11-long-wait-${i + 1}`); - - // Try clicking anywhere on the screen - cy.get('body').click({ force: true }); - cy.wait(2000); - takeScreenshot(`12-after-body-click-${i + 1}`); - } - - takeScreenshot('13-final-state'); - }); -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_video_capture.cy.js b/task-launcher/cypress/e2e/memory_video_capture.cy.js deleted file mode 100644 index 3fbe0c3e3..000000000 --- a/task-launcher/cypress/e2e/memory_video_capture.cy.js +++ /dev/null @@ -1,70 +0,0 @@ -const memory_game_url = 'http://localhost:8082/?task=memory-game'; - -describe('Memory Game Video Capture', () => { - // Handle all uncaught exceptions to prevent test failure - Cypress.on('uncaught:exception', (err, runnable) => { - console.log('Caught exception:', err.message); - return false; - }); - - it('records video of complete memory game session', () => { - cy.visit(memory_game_url); - - // Wait for OK button to appear (same as working memory test) - cy.contains('OK', { timeout: 300000 }).should('be.visible'); - cy.contains('OK').click({ force: true }); // start the game, force click to bypass webpack overlay - - // Wait for game to initialize - cy.get('p').then(() => { - // Play a few rounds of the memory game - playMemoryRounds(3); // Play 3 rounds instead of the full game - }); - - // Exit the game - cy.contains('Exit', { timeout: 30000 }).click({ force: true }); - }); -}); - -function playMemoryRounds(maxRounds) { - let roundCount = 0; - - function playRound() { - if (roundCount >= maxRounds) { - return; // Stop after max rounds - } - - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - // Instructions screen - cy.contains('OK').click({ force: true }); - } else { - // Game screen - wait for sequence to play then respond - cy.wait(3000); // Wait for sequence to finish - - // Click some blocks (not necessarily correct, just for demo) - cy.wrap(corsiBlocks[0]).click({ force: true }); - if (corsiBlocks.length > 1) { - cy.wait(500); - cy.wrap(corsiBlocks[1]).click({ force: true }); - } - - roundCount++; - } - }); - - // Check if game is still running - cy.get('p,h1').then((elements) => { - const text = elements.text(); - if (text.includes('Thank you!') || text.includes('Complete')) { - return; // Game finished - } else if (roundCount < maxRounds) { - cy.wait(2000); - playRound(); // Continue to next round - } - }); - } - - playRound(); -} \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_with_screenshots.cy.js b/task-launcher/cypress/e2e/memory_with_screenshots.cy.js deleted file mode 100644 index d052197fd..000000000 --- a/task-launcher/cypress/e2e/memory_with_screenshots.cy.js +++ /dev/null @@ -1,101 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('Memory Game with Screenshots', () => { - let screenshotCounter = 1; - - function takeScreenshot(description) { - cy.screenshot(`${screenshotCounter.toString().padStart(3, '0')}-${description}`); - screenshotCounter++; - } - - it('plays memory game and captures screenshots throughout', () => { - cy.visit(memory_game_url); - - takeScreenshot('initial-load'); - - // wait for OK button to appear - cy.contains('OK', { timeout: 300000 }).should('be.visible'); - takeScreenshot('ok-button-visible'); - - cy.contains('OK').realClick(); // start fullscreen - takeScreenshot('after-fullscreen-start'); - - cy.get('p').then(() => { - memoryLoop(); - }); - - takeScreenshot('before-exit'); - cy.contains('Exit').click(); - takeScreenshot('final-exit'); - }); - - function handleInstructions() { - takeScreenshot('instructions-screen'); - - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length === 0) { - cy.contains('OK').click(); - takeScreenshot('after-instructions-ok'); - } - }); - return; - } - - function answerTrial() { - takeScreenshot('trial-start'); - - // wait for gap after display phase - cy.get('p', { timeout: 20000 }).should('not.exist'); - cy.get('p').should('exist'); - - takeScreenshot('after-sequence-display'); - - cy.get('.jspsych-content').then((content) => { - const blocks = content.find('.jspsych-corsi-block'); - - if (blocks.length > 0) { - takeScreenshot('blocks-visible-for-input'); - - // wait for window to contain sequence information - cy.window().its('cypressData').should('have.property', 'correctAnswer'); - - cy.window().then((window) => { - const sequence = window.cypressData.correctAnswer; - - sequence.forEach((number, index) => { - blocks[number].click(); - takeScreenshot(`clicking-block-${index + 1}-of-${sequence.length}`); - }); - - cy.get('p').should('not.exist', { timeout: 5000 }); - takeScreenshot('trial-completed'); - }); - } - }); - return; - } - - function memoryLoop() { - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - - if (corsiBlocks.length > 0) { - answerTrial(); - } else { - handleInstructions(); - } - }); - - // end recursion if the task has reached the end screen - cy.get('p,h1').then((p) => { - if (p[0].textContent.includes('Thank you!')) { - takeScreenshot('thank-you-screen'); - return; - } else { - memoryLoop(); - } - }); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/memory_with_screenshots_fixed.cy.js b/task-launcher/cypress/e2e/memory_with_screenshots_fixed.cy.js deleted file mode 100644 index 41eae2f38..000000000 --- a/task-launcher/cypress/e2e/memory_with_screenshots_fixed.cy.js +++ /dev/null @@ -1,191 +0,0 @@ -const memory_game_url = 'http://localhost:8080/?task=memory-game'; - -describe('test memory game with screenshots (fixed)', () => { - let screenshotCounter = 0; - let lastContentHash = ''; - let lastScreenshotTime = 0; - - // Handle all uncaught exceptions to prevent test failure - Cypress.on('uncaught:exception', (err, runnable) => { - // Don't fail the test on uncaught exceptions - console.log('Caught exception:', err.message); - return false; - }); - - // Helper function to generate screenshot filename with counter - function getScreenshotName(description = '') { - screenshotCounter++; - const paddedCounter = screenshotCounter.toString().padStart(3, '0'); - const cleanDescription = description.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(); - return `${paddedCounter}_memory_${cleanDescription || 'screenshot'}`; - } - - // Combined function to handle both content change and time-based screenshots - function takeSmartScreenshot(description = '') { - cy.get('body').then(($body) => { - const currentContent = $body.html(); - const currentHash = btoa(currentContent).slice(0, 20); - const currentTime = Date.now(); - - // Take screenshot if content changed OR if 5 seconds have passed - if (currentHash !== lastContentHash) { - cy.screenshot(getScreenshotName(description + '_content_changed')); - lastContentHash = currentHash; - lastScreenshotTime = currentTime; - } else if (currentTime - lastScreenshotTime >= 5000) { - cy.screenshot(getScreenshotName(description + '_5sec_interval')); - lastScreenshotTime = currentTime; - } - }); - } - - it('visits memory game and plays it with screenshots', () => { - lastScreenshotTime = Date.now(); - - cy.visit(memory_game_url); - cy.screenshot(getScreenshotName('initial_visit')); - - // wait for OK button to appear - cy.contains('OK', { timeout: 300000 }).should('be.visible'); - cy.screenshot(getScreenshotName('ok_button_visible')); - - // Use regular click instead of realClick to avoid fullscreen issues - cy.contains('OK').click({ force: true }); - cy.screenshot(getScreenshotName('after_ok_click')); - - // Wait for game to start and handle any transitions - cy.wait(3000); - cy.screenshot(getScreenshotName('after_wait')); - - // More robust way to detect game start - cy.get('body').then(() => { - cy.screenshot(getScreenshotName('game_started')); - memoryLoopWithScreenshots(); - }); - }); - - function handleInstructionsWithScreenshots() { - takeSmartScreenshot('handle_instructions_start'); - - cy.get('body').then(() => { - // Check if we have corsi blocks or instructions - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - const okButton = content.find('button:contains("OK")'); - - if (corsiBlocks.length === 0 && okButton.length > 0) { - cy.screenshot(getScreenshotName('instructions_no_blocks')); - cy.contains('OK').click({ force: true }); - cy.screenshot(getScreenshotName('instructions_ok_clicked')); - } else if (corsiBlocks.length > 0) { - cy.screenshot(getScreenshotName('instructions_with_blocks')); - } else { - cy.screenshot(getScreenshotName('instructions_other_content')); - } - }); - }); - - // Add delay and check for time-based screenshot - cy.wait(1000); - takeSmartScreenshot('instructions_end'); - return; - } - - function answerTrialWithScreenshots() { - cy.screenshot(getScreenshotName('answer_trial_start')); - - // Wait for any display phase to complete - cy.wait(2000); - cy.screenshot(getScreenshotName('after_display_wait')); - - cy.get('.jspsych-content').then((content) => { - const blocks = content.find('.jspsych-corsi-block'); - - if (blocks.length > 0) { - cy.screenshot(getScreenshotName('blocks_visible_for_response')); - - // Try to get sequence information, but continue even if not available - cy.window().then((window) => { - if (window.cypressData && window.cypressData.correctAnswer) { - const sequence = window.cypressData.correctAnswer; - cy.screenshot(getScreenshotName(`sequence_length_${sequence.length}`)); - - sequence.forEach((number, index) => { - if (blocks[number]) { - cy.screenshot(getScreenshotName(`before_click_block_${number}_step_${index + 1}`)); - cy.wrap(blocks[number]).click({ force: true }); - cy.screenshot(getScreenshotName(`after_click_block_${number}_step_${index + 1}`)); - - // Small delay between clicks - cy.wait(500); - takeSmartScreenshot(`during_sequence_step_${index + 1}`); - } - }); - } else { - // If no sequence data, just click the first few blocks as a fallback - cy.screenshot(getScreenshotName('no_sequence_data_fallback')); - for (let i = 0; i < Math.min(3, blocks.length); i++) { - cy.wrap(blocks[i]).click({ force: true }); - cy.wait(500); - cy.screenshot(getScreenshotName(`fallback_click_${i + 1}`)); - } - } - - cy.screenshot(getScreenshotName('trial_completed')); - }); - } else { - cy.screenshot(getScreenshotName('no_blocks_found')); - } - }); - - // Check for time-based screenshot after trial - cy.wait(1000); - takeSmartScreenshot('trial_end'); - return; - } - - function memoryLoopWithScreenshots() { - takeSmartScreenshot('memory_loop_start'); - - cy.get('body').then(() => { - cy.get('.jspsych-content').then((content) => { - const corsiBlocks = content.find('.jspsych-corsi-block'); - const hasBlocks = corsiBlocks.length > 0; - - if (hasBlocks) { - cy.screenshot(getScreenshotName('trial_phase_blocks_present')); - answerTrialWithScreenshots(); - } else { - cy.screenshot(getScreenshotName('instruction_phase_no_blocks')); - handleInstructionsWithScreenshots(); - } - }); - - // Add delay and smart screenshot before checking for end condition - cy.wait(2000); - takeSmartScreenshot('before_end_check'); - - // Check for end condition more robustly - cy.get('body').then(($body) => { - const bodyText = $body.text(); - if (bodyText.includes('Thank you!') || bodyText.includes('Exit') || bodyText.includes('Complete')) { - cy.screenshot(getScreenshotName('end_condition_detected')); - - // Try to click Exit if available - cy.get('body').then(() => { - if ($body.find('button:contains("Exit")').length > 0) { - cy.contains('Exit').click({ force: true }); - cy.screenshot(getScreenshotName('exit_clicked')); - } - }); - return; - } else { - cy.screenshot(getScreenshotName('continuing_loop')); - // Continue the loop with a timeout to prevent infinite recursion - cy.wait(1000); - memoryLoopWithScreenshots(); - } - }); - }); - } -}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/quick_test.cy.js b/task-launcher/cypress/e2e/quick_test.cy.js new file mode 100644 index 000000000..788851cf7 --- /dev/null +++ b/task-launcher/cypress/e2e/quick_test.cy.js @@ -0,0 +1,44 @@ +describe('Quick Memory Game Test', () => { + it('captures screenshots from memory game', () => { + // Visit with fullscreen mocking + cy.visit('http://localhost:8080/?task=memory-game', { + timeout: 30000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Take initial screenshot + cy.screenshot('01-initial-load'); + cy.wait(5000); + + // Take screenshots every 10 seconds for 1 minute + for (let i = 1; i <= 6; i++) { + cy.wait(10000); + cy.screenshot(`${(i+1).toString().padStart(2, '0')}-interval-${i}`); + + // Try to click any buttons that appear + cy.get('body').then(($body) => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } else if ($body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + cy.screenshot('08-final'); + }); +}); diff --git a/task-launcher/cypress/e2e/test_memory-game.cy.js b/task-launcher/cypress/e2e/test_memory-game.cy.js new file mode 100644 index 000000000..b29613ae9 --- /dev/null +++ b/task-launcher/cypress/e2e/test_memory-game.cy.js @@ -0,0 +1,60 @@ +describe('memory-game Complete Run', () => { + it('runs complete task with screenshots', () => { + cy.visit('http://localhost:8080/?task=memory-game', { timeout: 60000 }); + + // Mock fullscreen + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + let counter = 0; + + // Initial screenshot + cy.screenshot(`${String(counter++).padStart(2, '0')}-start`); + cy.wait(2000); + + // Start task + cy.get('body').then($body => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-clicked-ok`); + } + }); + + // Main loop - run for reasonable time taking screenshots + for (let i = 0; i < 20; i++) { + cy.wait(8000); + cy.screenshot(`${String(counter++).padStart(2, '0')}-step-${i}`); + + // Check if completed + cy.get('body').then($body => { + const text = $body.text().toLowerCase(); + if (text.includes('thank you') || text.includes('complete') || text.includes('exit')) { + return; // Done + } + + // Continue interacting + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('.jspsych-corsi-block').length > 0) { + cy.get('.jspsych-corsi-block').then($blocks => { + cy.wrap($blocks[Math.floor(Math.random() * $blocks.length)]).click({ force: true }); + }); + } else if ($body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then($buttons => { + cy.wrap($buttons[Math.floor(Math.random() * $buttons.length)]).click({ force: true }); + }); + } else if ($body.find('button').length > 0) { + cy.get('button').first().click({ force: true }); + } + }); + } + + cy.screenshot(`${String(counter++).padStart(2, '0')}-final`); + }); +}); diff --git a/task-launcher/cypress/e2e/test_single_task.cy.js b/task-launcher/cypress/e2e/test_single_task.cy.js new file mode 100644 index 000000000..0147d14d7 --- /dev/null +++ b/task-launcher/cypress/e2e/test_single_task.cy.js @@ -0,0 +1,67 @@ +describe('Single Task Test - Memory Game', () => { + it('should capture screenshots with interactions', () => { + // Mock fullscreen API to prevent errors + cy.visit('http://localhost:8080/?task=memory-game', { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + } + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Wait for initial load + cy.wait(3000); + + // Take screenshots at intervals with interactions + for (let i = 0; i < 10; i++) { + // Try to interact with common elements + cy.get('body').then(($body) => { + const selectors = [ + 'button:contains("OK")', + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("Start")', + '.jspsych-btn', + 'button[type="button"]', + '.btn', + 'button' + ]; + + let clicked = false; + for (const selector of selectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().then(($el) => { + if ($el.is(':visible') && !clicked) { + cy.wrap($el).click({ force: true }); + clicked = true; + } + }); + break; + } + } + + // If no buttons found, click randomly + if (!clicked) { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Wait and take screenshot + cy.wait(5000); + cy.screenshot(`${String(i + 1).padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); \ No newline at end of file diff --git a/task-launcher/cypress/e2e/working_test.cy.js b/task-launcher/cypress/e2e/working_test.cy.js new file mode 100644 index 000000000..0a26c5374 --- /dev/null +++ b/task-launcher/cypress/e2e/working_test.cy.js @@ -0,0 +1,40 @@ +describe('Working Test with Correct Port', () => { + // Handle all uncaught exceptions + Cypress.on('uncaught:exception', (err, runnable) => { + console.log('Caught exception:', err.message); + return false; + }); + + it('should work with port 8081', () => { + // Visit with correct port + cy.visit('http://localhost:8080/?task=egma-math'); + + // Take initial screenshot + cy.screenshot('01-initial-load'); + cy.wait(3000); + + // Look for OK button and click it + cy.get('body').then(($body) => { + cy.screenshot('02-page-content'); + + if ($body.find('button:contains("OK")').length > 0) { + cy.contains('OK').click({ force: true }); + cy.screenshot('03-after-ok-click'); + cy.wait(3000); + } + + // Take more screenshots + cy.screenshot('04-current-state'); + + // Try to interact with any buttons + const buttons = $body.find('button:not(:disabled)'); + if (buttons.length > 0) { + cy.wrap(buttons.first()).click({ force: true }); + cy.screenshot('05-after-button-click'); + } + }); + + cy.wait(3000); + cy.screenshot('06-final-state'); + }); +}); \ No newline at end of file diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/002-02-game-started.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/002-02-game-started.png deleted file mode 100644 index d16d86abb..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/002-02-game-started.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/003-03-game-state-1.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/003-03-game-state-1.png deleted file mode 100644 index 4cea9b1b2..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/003-03-game-state-1.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/004-04-game-state-2.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/004-04-game-state-2.png deleted file mode 100644 index ccca2bb71..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/004-04-game-state-2.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/005-05-game-state-3.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/005-05-game-state-3.png deleted file mode 100644 index 306165a35..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/005-05-game-state-3.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/006-06-game-state-4.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/006-06-game-state-4.png deleted file mode 100644 index 5bdeecaea..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/006-06-game-state-4.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/007-07-game-state-5.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/007-07-game-state-5.png deleted file mode 100644 index 8376cf8e7..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/007-07-game-state-5.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/008-08-game-state-6.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/008-08-game-state-6.png deleted file mode 100644 index cb076d2ce..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/008-08-game-state-6.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/009-09-game-state-7.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/009-09-game-state-7.png deleted file mode 100644 index 8376cf8e7..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/009-09-game-state-7.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/010-10-game-state-8.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/010-10-game-state-8.png deleted file mode 100644 index cb076d2ce..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/010-10-game-state-8.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/011-11-game-state-9.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/011-11-game-state-9.png deleted file mode 100644 index 8376cf8e7..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/011-11-game-state-9.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/012-12-game-state-10.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/012-12-game-state-10.png deleted file mode 100644 index cb076d2ce..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/012-12-game-state-10.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/013-13-game-state-11.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/013-13-game-state-11.png deleted file mode 100644 index 8376cf8e7..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/013-13-game-state-11.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/014-14-game-state-12.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/014-14-game-state-12.png deleted file mode 100644 index cb076d2ce..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/014-14-game-state-12.png and /dev/null differ diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/015-99-final-state.png b/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/015-99-final-state.png deleted file mode 100644 index 6b290f554..000000000 Binary files a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/015-99-final-state.png and /dev/null differ diff --git a/extracted_screenshots/raw_frames/01_initial_page.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-001-initial-load.png similarity index 100% rename from extracted_screenshots/raw_frames/01_initial_page.png rename to task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-001-initial-load.png diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-002-no-start-button.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-002-no-start-button.png new file mode 100644 index 000000000..73c93d39d Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-002-no-start-button.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-003-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-003-no-stimulus.png new file mode 100644 index 000000000..be380d4bc Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-003-no-stimulus.png differ diff --git a/extracted_screenshots/raw_frames/02_ok_button_visible.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-004-no-stimulus.png similarity index 100% rename from extracted_screenshots/raw_frames/02_ok_button_visible.png rename to task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-004-no-stimulus.png diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-005-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-005-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-005-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-006-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-006-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-006-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-007-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-007-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-007-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-008-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-008-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-008-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-009-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-009-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-009-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-010-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-010-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-010-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-011-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-011-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-011-no-stimulus.png differ diff --git a/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-012-no-stimulus.png b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-012-no-stimulus.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/task-launcher/cypress/screenshots/temp_single_task.cy.js/2025-07-02T16-46-15-egma-math-012-no-stimulus.png differ diff --git a/task-launcher/cypress/videos/memory_simple_complete.cy.js-compressed.mp4 b/task-launcher/cypress/videos/memory_simple_complete.cy.js-compressed.mp4 deleted file mode 100644 index b48d040f2..000000000 Binary files a/task-launcher/cypress/videos/memory_simple_complete.cy.js-compressed.mp4 and /dev/null differ diff --git a/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4 b/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4 deleted file mode 100644 index 51e53dc76..000000000 Binary files a/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4 and /dev/null differ diff --git a/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4.meta b/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4.meta deleted file mode 100644 index bb4f75233..000000000 --- a/task-launcher/cypress/videos/memory_simple_complete.cy.js.mp4.meta +++ /dev/null @@ -1,6 +0,0 @@ -;FFMETADATA1 -[CHAPTER] -TIMEBASE=1/1000 -START=-154114 -END=3168 -title=Memory Game Simple Complete Run runs memory game with regular screenshots \ No newline at end of file diff --git a/task-launcher/cypress_egma-math_2025-07-02_09-46-04.log b/task-launcher/cypress_egma-math_2025-07-02_09-46-04.log new file mode 100644 index 000000000..d23547088 --- /dev/null +++ b/task-launcher/cypress_egma-math_2025-07-02_09-46-04.log @@ -0,0 +1,28 @@ + +DevTools listening on ws://127.0.0.1:45219/devtools/browser/22da36af-f757-4d30-8494-878ce4f58663 +(node:26232) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:26232) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +========================================= + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - egma-math diff --git a/task-launcher/cypress_matrix-reasoning_2025-07-01_21-32-40.log b/task-launcher/cypress_matrix-reasoning_2025-07-01_21-32-40.log new file mode 100644 index 000000000..0543794e3 --- /dev/null +++ b/task-launcher/cypress_matrix-reasoning_2025-07-01_21-32-40.log @@ -0,0 +1,86 @@ + +DevTools listening on ws://127.0.0.1:35587/devtools/browser/a2e48b30-c2e9-46ca-ad22-5989edcd4e4f +(node:30170) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:30170) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +======================================= + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - matrix-reasoning + 1) captures complete matrix-reasoning task until completion + + + 0 passing (1m) + 1 failing + + 1) Single Task Capture - matrix-reasoning + captures complete matrix-reasoning task until completion: + AssertionError: Timed out retrying after 60000ms: Expected to find content: 'OK' but never did. + at Context.eval (webpack://@levante-framework/core-tasks/./cypress/e2e/temp_single_task.cy.js:35:42) + + + + + (Results) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Tests: 1 โ”‚ + โ”‚ Passing: 0 โ”‚ + โ”‚ Failing: 1 โ”‚ + โ”‚ Pending: 0 โ”‚ + โ”‚ Skipped: 0 โ”‚ + โ”‚ Screenshots: 2 โ”‚ + โ”‚ Video: true โ”‚ + โ”‚ Duration: 1 minute, 5 seconds โ”‚ + โ”‚ Spec Ran: temp_single_task.cy.js โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + + (Screenshots) + + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x660) + k.cy.js/2025-07-02T04-32-47-matrix-reasoning-001-initial-load.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1280x720) + k.cy.js/Single Task Capture - matrix-reasoning -- captures complete matrix-reaso + ning task until completion (failed).png + + + (Video) + + - Started compressing: Compressing to 32 CRF + - Finished compressing: 2 seconds + + - Video output: /home/david/levante/core-tasks/task-launcher/cypress/videos/temp_single_task.cy.js.mp4 + + +resize: unknown character 0xa, exiting. +================================================================================ + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โœ– temp_single_task.cy.js 01:05 1 - 1 - - โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โœ– 1 of 1 failed (100%) 01:05 1 - 1 - - + diff --git a/task-launcher/cypress_matrix-reasoning_2025-07-01_21-36-33.log b/task-launcher/cypress_matrix-reasoning_2025-07-01_21-36-33.log new file mode 100644 index 000000000..83afc0907 --- /dev/null +++ b/task-launcher/cypress_matrix-reasoning_2025-07-01_21-36-33.log @@ -0,0 +1,28 @@ + +DevTools listening on ws://127.0.0.1:36563/devtools/browser/148cfb4d-ca8c-412a-8a3e-3f8c36aa0b60 +(node:32220) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:32220) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +==================================================================================================== + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - matrix-reasoning diff --git a/task-launcher/cypress_matrix-reasoning_2025-07-01_22-05-23.log b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-05-23.log new file mode 100644 index 000000000..16e634f2a --- /dev/null +++ b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-05-23.log @@ -0,0 +1,44 @@ + +DevTools listening on ws://127.0.0.1:44725/devtools/browser/7cdde9f6-a3a6-459e-b088-8a1e0ca1484f +(node:15960) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:15960) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +================================== + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - matrix-reasoning +The Test Runner unexpectedly exited via a exit event with signal SIGKILL + +Please search Cypress documentation for possible solutions: + +https://on.cypress.io + +Check if there is a GitHub issue describing this crash: + +https://github.com/cypress-io/cypress/issues + +Consider opening a new issue. + +---------- + +Platform: linux-x64 (Ubuntu - 24.04) +Cypress Version: 13.13.3 diff --git a/task-launcher/cypress_matrix-reasoning_2025-07-01_22-08-40.log b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-08-40.log new file mode 100644 index 000000000..77d6a38ec --- /dev/null +++ b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-08-40.log @@ -0,0 +1,28 @@ + +DevTools listening on ws://127.0.0.1:44549/devtools/browser/9237d006-b0d1-4690-93df-dea8a4c4dadc +(node:18304) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:18304) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +==================================================================================================== + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - matrix-reasoning diff --git a/task-launcher/cypress_matrix-reasoning_2025-07-01_22-12-07.log b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-12-07.log new file mode 100644 index 000000000..fb8420c5e --- /dev/null +++ b/task-launcher/cypress_matrix-reasoning_2025-07-01_22-12-07.log @@ -0,0 +1,28 @@ + +DevTools listening on ws://127.0.0.1:41853/devtools/browser/06952be7-7c99-4ba9-ad33-564c4dc2dd94 +(node:21700) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:21700) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +==================================================================================================== + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - matrix-reasoning diff --git a/task-launcher/cypress_theory-of-mind_2025-07-01_21-13-10.log b/task-launcher/cypress_theory-of-mind_2025-07-01_21-13-10.log new file mode 100644 index 000000000..e9b55d0ea --- /dev/null +++ b/task-launcher/cypress_theory-of-mind_2025-07-01_21-13-10.log @@ -0,0 +1,233 @@ + +DevTools listening on ws://127.0.0.1:41257/devtools/browser/dab48678-410f-4244-8122-a86e08cab896 +(node:21976) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`: +--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///home/david/.cache/Cypress/13.13.3/Cypress/resources/app/node_modules/ts-node/esm/transpile-only.mjs", pathToFileURL("./"));' +(Use `node --trace-warnings ...` to show where the warning was created) +(node:21976) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated. +(Use `node --trace-deprecation ...` to show where the warning was created) + +================================================== + + (Run Starting) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Cypress: 13.13.3 โ”‚ + โ”‚ Browser: Electron 118 (headless) โ”‚ + โ”‚ Node Version: v22.14.0 (/home/david/.nvm/versions/node/v22.14.0/bin/node) โ”‚ + โ”‚ Specs: 1 found (temp_single_task.cy.js) โ”‚ + โ”‚ Searched: cypress/e2e/temp_single_task.cy.js โ”‚ + โ”‚ Experiments: experimentalMemoryManagement=true โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + Running: temp_single_task.cy.js (1 of 1) + + + Single Task Capture - theory-of-mind + โœ“ captures complete theory-of-mind task until completion (616424ms) + + + 1 passing (10m) + + + (Results) + + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Tests: 1 โ”‚ + โ”‚ Passing: 1 โ”‚ + โ”‚ Failing: 0 โ”‚ + โ”‚ Pending: 0 โ”‚ + โ”‚ Skipped: 0 โ”‚ + โ”‚ Screenshots: 80 โ”‚ + โ”‚ Video: true โ”‚ + โ”‚ Duration: 10 minutes, 17 seconds โ”‚ + โ”‚ Spec Ran: temp_single_task.cy.js โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + + (Screenshots) + + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x660) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-001-initial-load.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-002-task-started.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-003-step-1.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-004-step-2.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-005-step-3.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-006-step-4.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-007-step-5.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-008-step-6.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-009-step-7.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-010-step-8.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-011-step-9.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-012-step-10.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-013-step-11.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-014-step-12.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-015-step-13.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-016-step-14.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-017-step-15.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-018-step-16.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-019-step-17.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-020-step-18.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-021-step-19.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-022-step-20.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-023-step-21.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-024-step-22.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-025-step-23.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-026-step-24.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-027-step-25.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-028-step-26.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-029-step-27.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-030-step-28.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-031-step-29.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-032-step-30.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-033-step-31.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-034-step-32.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-035-step-33.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-036-step-34.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-037-step-35.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-038-step-36.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-039-step-37.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-040-step-38.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-041-step-39.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-042-step-40.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-043-step-41.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-044-step-42.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-045-step-43.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-046-step-44.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-047-step-45.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-048-step-46.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-049-step-47.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-050-step-48.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-051-step-49.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-052-step-50.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-053-step-51.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-054-step-52.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-055-step-53.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-056-step-54.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-057-step-55.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-058-step-56.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-059-step-57.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-060-step-58.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-061-step-59.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-062-step-60.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-063-step-61.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-064-step-62.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-065-step-63.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-066-step-64.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-067-step-65.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-068-step-66.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-069-step-67.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-070-step-68.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-071-step-69.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-072-step-70.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-073-step-71.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-074-step-72.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-075-step-73.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-076-step-74.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-077-step-75.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-078-step-76.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-079-step-77.png + - /home/david/levante/core-tasks/task-launcher/cypress/screenshots/temp_single_tas (1000x680) + k.cy.js/2025-07-02T04-13-18-theory-of-mind-080-task-completed.png + + + (Video) + + - Started compressing: Compressing to 32 CRF + - Finished compressing: 32 seconds + Compression progress: 100% + + - Video output: /home/david/levante/core-tasks/task-launcher/cypress/videos/temp_single_task.cy.js.mp4 + + +================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โœ” temp_single_task.cy.js 10:17 1 1 - - - โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โœ” All specs passed! 10:17 1 1 - - - + diff --git a/task-launcher/enhanced_interaction_template.js b/task-launcher/enhanced_interaction_template.js new file mode 100644 index 000000000..231edeeaa --- /dev/null +++ b/task-launcher/enhanced_interaction_template.js @@ -0,0 +1,80 @@ +// Enhanced interaction strategy for all Levante framework tasks +// This template provides comprehensive interaction logic for: +// - Navigation buttons +// - Sliders and input fields +// - Multiple choice questions +// - Audio controls +// - Fallback strategies + +const enhancedInteractionLogic = ` + // Enhanced interaction strategy for Levante framework tasks + cy.get('body').then(($body) => { + // Strategy 1: Continue/OK/Next/Start buttons (highest priority) + if ($body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin")').first().click(); + cy.wait(1000); // Brief pause after navigation + } + // Strategy 2: Number line slider interactions + else if ($body.find('input[type="range"], .slider, .number-line').length > 0) { + // Interact with sliders by setting random values + cy.get('input[type="range"], .slider input').then(($sliders) => { + if ($sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap($sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + // Look for submit button after slider interaction + if ($body.find('button:contains("Submit"), button:contains("Done"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Done"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 3: Multiple choice responses (task questions) + else if ($body.find('[data-choice], .choice-button, .response-button, .answer-choice').length > 0) { + // Click random answer choice + cy.get('[data-choice], .choice-button, .response-button, .answer-choice').then(($choices) => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices.eq(randomIndex)).click(); + }); + } + // Strategy 4: Number input fields + else if ($body.find('input[type="number"], input[type="text"]').length > 0) { + cy.get('input[type="number"], input[type="text"]').then(($inputs) => { + if ($inputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap($inputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + // Look for submit after input + if ($body.find('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').length > 0) { + cy.get('button:contains("Submit"), button:contains("Enter"), button:not([disabled])').first().click(); + } + } + }); + } + // Strategy 5: Audio response buttons (listen/play again) + else if ($body.find('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').length > 0) { + cy.get('button:contains("Play"), button:contains("Listen"), button:contains("Replay"), .audio-button').first().click(); + cy.wait(2000); // Wait for audio to play + } + // Strategy 6: Any enabled buttons (fallback) + else if ($body.find('button:not([disabled]):visible').length > 0) { + cy.get('button:not([disabled]):visible').first().click(); + } + // Strategy 7: Clickable elements with data attributes + else if ($body.find('[data-testid], [data-cy], .clickable, .btn').length > 0) { + cy.get('[data-testid], [data-cy], .clickable, .btn').first().click(); + } + // Strategy 8: Random button selection for multiple choice tasks + else if ($body.find('button').length >= 2) { + // Randomly click one of the available buttons + const buttonIndex = Math.floor(Math.random() * $body.find('button').length); + cy.get('button').eq(buttonIndex).click(); + } + // Strategy 9: Try pressing Enter key to advance + else { + cy.get('body').type('{enter}'); + } + }); +`; + +module.exports = enhancedInteractionLogic; \ No newline at end of file diff --git a/task-launcher/extracted_screenshots/screenshot_summary.html b/task-launcher/extracted_screenshots/screenshot_summary.html deleted file mode 100644 index 816270a77..000000000 --- a/task-launcher/extracted_screenshots/screenshot_summary.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - Memory Game Screenshots - - - -

Memory Game Screenshots

-

Extracted from: cypress/videos/memory_passive_capture.cy.js.mp4

-

Total screenshots: 33

-
-
- screenshot_0001.png -

screenshot_0001.png

-
-
- screenshot_0002.png -

screenshot_0002.png

-
-
- screenshot_0003.png -

screenshot_0003.png

-
-
- screenshot_0004.png -

screenshot_0004.png

-
-
- screenshot_0005.png -

screenshot_0005.png

-
-
- screenshot_0006.png -

screenshot_0006.png

-
-
- screenshot_0007.png -

screenshot_0007.png

-
-
- screenshot_0008.png -

screenshot_0008.png

-
-
- screenshot_0009.png -

screenshot_0009.png

-
-
- screenshot_0010.png -

screenshot_0010.png

-
-
- screenshot_0011.png -

screenshot_0011.png

-
-
- screenshot_0012.png -

screenshot_0012.png

-
-
- screenshot_0013.png -

screenshot_0013.png

-
-
- screenshot_0014.png -

screenshot_0014.png

-
-
- screenshot_0015.png -

screenshot_0015.png

-
-
- screenshot_0016.png -

screenshot_0016.png

-
-
- screenshot_0017.png -

screenshot_0017.png

-
-
- screenshot_0018.png -

screenshot_0018.png

-
-
- screenshot_0019.png -

screenshot_0019.png

-
-
- screenshot_0020.png -

screenshot_0020.png

-
-
- screenshot_0021.png -

screenshot_0021.png

-
-
- screenshot_0022.png -

screenshot_0022.png

-
-
- screenshot_0023.png -

screenshot_0023.png

-
-
- screenshot_0024.png -

screenshot_0024.png

-
-
- screenshot_0025.png -

screenshot_0025.png

-
-
- screenshot_0026.png -

screenshot_0026.png

-
-
- screenshot_0027.png -

screenshot_0027.png

-
-
- screenshot_0028.png -

screenshot_0028.png

-
-
- screenshot_0029.png -

screenshot_0029.png

-
-
- screenshot_0030.png -

screenshot_0030.png

-
-
- screenshot_0031.png -

screenshot_0031.png

-
-
- screenshot_0032.png -

screenshot_0032.png

-
-
- screenshot_0033.png -

screenshot_0033.png

-
- - diff --git a/task-launcher/fix_ports.sh b/task-launcher/fix_ports.sh new file mode 100755 index 000000000..342923686 --- /dev/null +++ b/task-launcher/fix_ports.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +echo "๐Ÿ”ง Fixing port references from 8080 to 8081 in all Cypress test files..." + +# Find and replace in all .cy.js files +find . -name "*.cy.js" -type f -exec sed -i 's/localhost:8080/localhost:8081/g' {} + + +echo "โœ… Port fix complete! All Cypress tests now use port 8081" + +# Show a summary of what was changed +echo "๐Ÿ“Š Files that were updated:" +find . -name "*.cy.js" -type f -exec grep -l "localhost:8081" {} + | wc -l | xargs echo "Total files with correct port:" + +# Verify the change worked +echo "" +echo "๐Ÿ” Verification - checking for any remaining 8080 references:" +remaining=$(find . -name "*.cy.js" -type f -exec grep -l "localhost:8080" {} + 2>/dev/null | wc -l) +if [ "$remaining" -eq 0 ]; then + echo "โœ… All port references successfully updated!" +else + echo "โš ๏ธ Found $remaining files still using port 8080:" + find . -name "*.cy.js" -type f -exec grep -l "localhost:8080" {} + 2>/dev/null +fi \ No newline at end of file diff --git a/task-launcher/quick_single_task.sh b/task-launcher/quick_single_task.sh new file mode 100755 index 000000000..a00fdabba --- /dev/null +++ b/task-launcher/quick_single_task.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Quick test for a single task screenshot capture + +echo "๐ŸŽฏ QUICK SINGLE TASK TEST" +echo "=========================" + +TASK="memory-game" +echo "๐Ÿ“ธ Testing screenshot capture for: $TASK" + +# Kill any existing servers +pkill -f "webpack serve" 2>/dev/null || true +lsof -ti:8080 | xargs kill -9 2>/dev/null || true +sleep 2 + +# Start webpack dev server +echo "๐Ÿš€ Starting webpack dev server..." +npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & +SERVER_PID=$! + +# Wait for server +echo "โณ Waiting for server to start..." +sleep 15 + +# Check if server is running +if ! curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โŒ Server failed to start" + exit 1 +fi +echo "โœ… Server is running" + +# Create simple Cypress test +TEST_FILE="cypress/e2e/quick_test.cy.js" +mkdir -p cypress/e2e + +cat > "$TEST_FILE" << 'EOF' +describe('Quick Memory Game Test', () => { + it('captures screenshots from memory game', () => { + // Visit with fullscreen mocking + cy.visit('http://localhost:8080/?task=memory-game', { + timeout: 30000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Take initial screenshot + cy.screenshot('01-initial-load'); + cy.wait(5000); + + // Take screenshots every 10 seconds for 1 minute + for (let i = 1; i <= 6; i++) { + cy.wait(10000); + cy.screenshot(`${(i+1).toString().padStart(2, '0')}-interval-${i}`); + + // Try to click any buttons that appear + cy.get('body').then(($body) => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } else if ($body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + cy.screenshot('08-final'); + }); +}); +EOF + +# Run Cypress test +echo "๐Ÿƒ Running Cypress test..." +if npx cypress run --spec "$TEST_FILE" --record false; then + echo "โœ… Test completed successfully!" + + # Show results + echo "" + echo "๐Ÿ“Š RESULTS:" + SCREENSHOT_DIR="cypress/screenshots/quick_test.cy.js" + if [ -d "$SCREENSHOT_DIR" ]; then + echo "Screenshots captured: $(ls "$SCREENSHOT_DIR"/*.png 2>/dev/null | wc -l)" + echo "Screenshot files:" + ls -la "$SCREENSHOT_DIR"/*.png 2>/dev/null | head -10 + else + echo "โŒ No screenshots directory found" + fi +else + echo "โŒ Test failed" +fi + +# Cleanup +echo "๐Ÿ›‘ Stopping server..." +kill $SERVER_PID 2>/dev/null || true + +echo "๐ŸŽ‰ Quick test complete!" \ No newline at end of file diff --git a/task-launcher/requirements_ocr.txt b/task-launcher/requirements_ocr.txt new file mode 100644 index 000000000..60626d2e2 --- /dev/null +++ b/task-launcher/requirements_ocr.txt @@ -0,0 +1,3 @@ +pillow>=9.0.0 +pytesseract>=0.3.10 +numpy>=1.21.0 \ No newline at end of file diff --git a/task-launcher/run_3_tasks_quick.sh b/task-launcher/run_3_tasks_quick.sh new file mode 100755 index 000000000..dec1f2103 --- /dev/null +++ b/task-launcher/run_3_tasks_quick.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Quick 3-Task Screenshot Capture +TASKS=("memory-game" "hearts-and-flowers" "matrix-reasoning") +BASE_URL="http://localhost:8080" + +echo "Starting quick 3-task capture..." + +for task in "${TASKS[@]}"; do + echo "Processing task: $task" + + # Create Cypress test + cat > "cypress/e2e/${task}_quick.cy.js" << EOF +describe('Quick Task Capture - ${task}', () => { + it('should capture screenshots', () => { + cy.visit('${BASE_URL}/?task=${task}', { + onBeforeLoad(win) { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + } + }); + + cy.screenshot('00-start'); + cy.wait(3000); + + // Take 10 screenshots with interactions + for (let i = 0; i < 10; i++) { + cy.get('body').then((\$body) => { + const selectors = [ + 'button:contains("OK")', 'button:contains("Continue")', + 'button:contains("Next")', 'button:contains("Start")', + '.jspsych-btn', 'button[type="button"]', '.btn', 'button' + ]; + + let clicked = false; + for (const selector of selectors) { + if (\$body.find(selector).length > 0) { + cy.get(selector).first().then((\$el) => { + if (\$el.is(':visible') && !clicked) { + cy.wrap(\$el).click({ force: true }); + clicked = true; + } + }); + break; + } + } + + if (!clicked) { + cy.get('body').click(500, 300, { force: true }); + } + }); + + cy.wait(5000); + cy.screenshot(\`\${String(i + 1).padStart(2, '0')}-step-\${i}\`); + } + + cy.screenshot('99-final'); + }); +}); +EOF + + # Run the test + echo "Running Cypress test for $task..." + npx cypress run --spec "cypress/e2e/${task}_quick.cy.js" --browser chrome --headless + + echo "Completed $task" + sleep 2 +done + +echo "All 3 tasks completed! Check cypress/screenshots/ for results" \ No newline at end of file diff --git a/task-launcher/run_all_tasks.sh b/task-launcher/run_all_tasks.sh new file mode 100755 index 000000000..2b9e8d43f --- /dev/null +++ b/task-launcher/run_all_tasks.sh @@ -0,0 +1,187 @@ +#!/bin/bash +# Comprehensive script to capture screenshots for all available tasks + +echo "๐ŸŽฏ COMPREHENSIVE TASK SCREENSHOT CAPTURE" +echo "==================================================" + +# Create timestamped backup directory for all results +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_DIR="all_task_screenshots_$TIMESTAMP" +echo "๐Ÿ“ Results will be preserved in: $BACKUP_DIR" + +# Task list from taskConfig.ts +TASKS=( + "egma-math" + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Start webpack dev server +echo "๐Ÿš€ Starting webpack dev server..." +pkill -f "webpack serve" 2>/dev/null || true +pkill -f "port 8080" 2>/dev/null || true +sleep 2 + +npx webpack serve --mode development --env dbmode=development --port 8080 & +SERVER_PID=$! +echo "โณ Waiting for dev server to start..." +sleep 15 + +# Function to create and run test for a task +run_task_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name//-/_}_complete.cy.js" + + echo "" + echo "==================== ${task_name^^} ====================" + echo "๐Ÿ“ธ Running screenshots for: $task_name" + + # Create Cypress test file + cat > "$test_file" << EOF +const ${task_name//-/_}_url = 'http://localhost:8080/?task=${task_name}'; + +describe('${task_name^} Complete Run', () => { + let screenshotCounter = 1; + + function takeScreenshot(description) { + cy.screenshot(\`\${screenshotCounter.toString().padStart(3, '0')}-\${description}\`); + screenshotCounter++; + } + + it('runs complete ${task_name} with screenshots', () => { + // Visit with fullscreen mocking and extended timeout + cy.visit(${task_name//-/_}_url, { + timeout: 60000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 8 minutes with screenshots every 10 seconds + const totalDuration = 8 * 60 * 1000; // 8 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) { + cy.wait(screenshotInterval); + takeScreenshot(\`interval-\${i.toString().padStart(2, '0')}\`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then((\$body) => { + // Click OK buttons if they exist + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Click Continue buttons if they exist + if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Click Next buttons if they exist + if (\$body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Click Start buttons if they exist + if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } + // Click any visible buttons as fallback + if (\$body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + takeScreenshot('final-state'); + }); +}); +EOF + + # Run Cypress test + echo "๐Ÿƒ Running Cypress test..." + if timeout 600 npx cypress run --spec "$test_file" --record false; then + echo "โœ… $task_name screenshots completed" + + # Run OCR cleanup (preserves duplicates in backup folder) + local screenshot_dir="cypress/screenshots/${task_name//-/_}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + echo "๐Ÿ” Running OCR cleanup for $task_name (duplicates preserved in backup)..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + echo "โœ… $task_name cleanup complete - unique screenshots kept, duplicates backed up" + else + echo "โš ๏ธ $task_name cleanup had issues" + fi + else + echo "โš ๏ธ No screenshots found for $task_name" + fi + else + echo "โŒ $task_name test failed or timed out" + fi + + sleep 3 +} + +# Cleanup function +cleanup() { + echo "" + echo "๐Ÿ›‘ Stopping dev server..." + kill $SERVER_PID 2>/dev/null || true + pkill -f "webpack serve" 2>/dev/null || true +} + +# Set trap to cleanup on exit +trap cleanup EXIT + +# Run tests for all tasks +successful=0 +failed=0 + +for task in "${TASKS[@]}"; do + if run_task_test "$task"; then + ((successful++)) + else + ((failed++)) + fi +done + +# Create comprehensive backup of all results +echo "" +echo "๐Ÿ“ฆ Creating comprehensive backup..." +if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots "$BACKUP_DIR" + echo "โœ… All screenshots backed up to: $BACKUP_DIR" +else + echo "โš ๏ธ No screenshots directory found" +fi + +# Final summary +echo "" +echo "==================================================" +echo "๐Ÿ“Š FINAL SUMMARY REPORT" +echo "==================================================" +echo "โœ… Successful tasks: $successful" +echo "โŒ Failed tasks: $failed" +echo "๐Ÿ“ Complete backup: $BACKUP_DIR" +echo "๐ŸŽ‰ Task screenshot capture complete!" + +exit 0 \ No newline at end of file diff --git a/task-launcher/run_all_tasks_batch.sh b/task-launcher/run_all_tasks_batch.sh new file mode 100755 index 000000000..68cd2b0b2 --- /dev/null +++ b/task-launcher/run_all_tasks_batch.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +# Batch Task Screenshot Capture - All 12 Tasks +# Uses the proven working simple script for each task + +# All 12 tasks +TASKS="hearts-and-flowers egma-math matrix-reasoning mental-rotation memory-game same-different-selection trog vocab theory-of-mind intro roar-inference adult-reasoning" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Create backup directory +BACKUP_DIR="all_tasks_batch_$(date +%Y%m%d_%H%M%S)" + +log "Starting batch task screenshot capture for all 12 tasks" +log "Tasks: $TASKS" +log "Results will be backed up to: $BACKUP_DIR" + +# Initialize counters +task_count=0 +total_tasks=$(echo $TASKS | wc -w) +successful_tasks=0 +failed_tasks=0 +total_screenshots=0 + +echo "========================================" +echo "BATCH TASK CAPTURE STARTING" +echo "========================================" + +# Process each task +for task in $TASKS; do + task_count=$((task_count + 1)) + + echo "" + log "Processing task $task_count/$total_tasks: $task" + echo "----------------------------------------" + + # Run the proven working script for this task + if ./run_tasks_simple.sh "$task"; then + success "Task $task completed successfully" + successful_tasks=$((successful_tasks + 1)) + + # Count screenshots for this task + screenshot_dir="cypress/screenshots/${task}_simple.cy.js" + if [[ -d "$screenshot_dir" ]]; then + count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + total_screenshots=$((total_screenshots + count)) + log "Task $task: $count unique screenshots captured" + fi + else + error "Task $task failed" + failed_tasks=$((failed_tasks + 1)) + fi + + log "Progress: $task_count/$total_tasks tasks completed" + echo "========================================" +done + +# Create comprehensive backup +log "Creating comprehensive backup..." +mkdir -p "$BACKUP_DIR" +cp -r cypress/screenshots/* "$BACKUP_DIR/" 2>/dev/null || true +cp -r cypress/videos/* "$BACKUP_DIR/" 2>/dev/null || true + +# Final summary +echo "" +echo "========================================" +echo "BATCH CAPTURE COMPLETE" +echo "========================================" +success "All tasks processed!" +log "Summary:" +log " Total tasks: $total_tasks" +log " Successful: $successful_tasks" +log " Failed: $failed_tasks" +log " Total unique screenshots: $total_screenshots" +log " Backup directory: $BACKUP_DIR" + +# Detailed results +echo "" +log "Detailed results by task:" +for task in $TASKS; do + screenshot_dir="cypress/screenshots/${task}_simple.cy.js" + if [[ -d "$screenshot_dir" ]]; then + count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + backup_count=$(ls "$screenshot_dir/duplicates_backup"/*.png 2>/dev/null | wc -l) + total_before_cleanup=$((count + backup_count)) + log " $task: $total_before_cleanup โ†’ $count screenshots ($(( backup_count > 0 ? backup_count : 0 )) duplicates removed)" + else + error " $task: No screenshots found" + fi +done + +if [[ $failed_tasks -eq 0 ]]; then + success "๐ŸŽ‰ All tasks completed successfully!" + success "๐Ÿ“ All results backed up to: $BACKUP_DIR" + success "๐Ÿ“Š Total unique screenshots captured: $total_screenshots" +else + warn "โš ๏ธ $failed_tasks tasks failed, but $successful_tasks completed successfully" + log "๐Ÿ“ Results for successful tasks backed up to: $BACKUP_DIR" +fi \ No newline at end of file diff --git a/task-launcher/run_all_tasks_complete.sh b/task-launcher/run_all_tasks_complete.sh new file mode 100755 index 000000000..7bba872cd --- /dev/null +++ b/task-launcher/run_all_tasks_complete.sh @@ -0,0 +1,328 @@ +#!/bin/bash + +# Complete Task Screenshot Capture - All 12 Tasks +# Improved interaction logic to prevent getting stuck + +set -e + +# Configuration +SCREENSHOT_INTERVAL=8 # seconds between screenshots +TASK_DURATION=300 # 5 minutes per task +MAX_TIMEOUT=360 # 6 minute maximum timeout per task + +# All 12 tasks +TASKS="hearts-and-flowers egma-math matrix-reasoning mental-rotation memory-game same-different-selection trog vocab theory-of-mind intro roar-inference adult-reasoning" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to check if webpack server is running +check_webpack_server() { + if curl -s http://localhost:8080 > /dev/null 2>&1; then + return 0 + else + return 1 + fi +} + +# Function to start webpack server if needed +ensure_webpack_server() { + if ! check_webpack_server; then + log "Starting webpack dev server..." + pkill -f "webpack.*8080" || true + sleep 2 + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + for i in {1..30}; do + if check_webpack_server; then + success "Webpack server is ready!" + return 0 + fi + sleep 1 + done + + error "Webpack server failed to start" + return 1 + else + success "Webpack server already running" + return 0 + fi +} + +# Function to generate improved Cypress test +generate_complete_test() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_complete.cy.js" + + log "Generating complete test for $task_name..." + + cat > "$test_file" << EOF +describe('$task_name Complete Task Capture', () => { + it('should capture screenshots while completing entire task', () => { + const taskName = '$task_name'; + const screenshotInterval = $SCREENSHOT_INTERVAL * 1000; + const maxDuration = $TASK_DURATION * 1000; + let screenshotCount = 0; + let lastInteractionTime = Date.now(); + let stuckCount = 0; + + // Visit task with all necessary mocking + cy.visit(\`http://localhost:8080/?task=\${taskName}\`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + + // Mock getUserMedia for tasks that might need it + win.navigator.mediaDevices = win.navigator.mediaDevices || {}; + win.navigator.mediaDevices.getUserMedia = cy.stub().resolves({ + getTracks: () => [] + }); + } + }); + + // Initial screenshot + cy.screenshot(\`00-start\`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-step-\${screenshotCount - 1}\`, { capture: 'viewport' }); + screenshotCount++; + + // Advanced interaction logic with fallbacks + cy.get('body').then(\$body => { + let interactionMade = false; + + // Priority 1: Completion or continue buttons + if (\$body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), .continue-btn, .ok-btn').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), .continue-btn, .ok-btn').first().click({ force: true }); + interactionMade = true; + } + // Priority 2: Task-specific interactions + else if (taskName === 'memory-game' && \$body.find('.corsi-block, .memory-block, [class*="corsi"], [id*="corsi"]').length > 0) { + cy.get('.corsi-block, .memory-block, [class*="corsi"], [id*="corsi"]').first().click({ force: true }); + interactionMade = true; + } + else if (taskName === 'hearts-and-flowers' && \$body.find('button[data-response], .response-button, [class*="response"]').length > 0) { + const buttons = \$body.find('button[data-response], .response-button, [class*="response"]'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button[data-response], .response-button, [class*="response"]').eq(randomIndex).click({ force: true }); + interactionMade = true; + } + // Priority 3: Multiple choice buttons (most common) + else if (\$body.find('#jspsych-html-multi-response-btngroup button, .jspsych-btn, button[data-choice]').length >= 2) { + const buttons = \$body.find('#jspsych-html-multi-response-btngroup button, .jspsych-btn, button[data-choice]'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button, .jspsych-btn, button[data-choice]').eq(randomIndex).click({ force: true }); + interactionMade = true; + } + // Priority 4: Slider inputs (math tasks) + else if (\$body.find('input[type="range"], .slider, [class*="slider"], .number-line').length > 0) { + cy.get('input[type="range"], .slider, [class*="slider"], .number-line').first().then(\$slider => { + const min = \$slider.attr('min') || 0; + const max = \$slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap(\$slider).invoke('val', randomValue).trigger('input').trigger('change'); + interactionMade = true; + }); + } + // Priority 5: Any enabled buttons + else if (\$body.find('button:not([disabled]):not(.disabled)').length > 0) { + const buttons = \$body.find('button:not([disabled]):not(.disabled)'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled]):not(.disabled)').eq(randomIndex).click({ force: true }); + interactionMade = true; + } + // Priority 6: Clickable elements + else if (\$body.find('[onclick], [data-click], .clickable, .selectable').length > 0) { + cy.get('[onclick], [data-click], .clickable, .selectable').first().click({ force: true }); + interactionMade = true; + } + // Priority 7: Input fields + else if (\$body.find('input[type="text"], input[type="number"], textarea').length > 0) { + cy.get('input[type="text"], input[type="number"], textarea').first().type('test', { force: true }); + interactionMade = true; + } + + // If no interaction was made, try some fallback strategies + if (!interactionMade) { + stuckCount++; + if (stuckCount > 3) { + // Try pressing common keys + cy.get('body').type('{enter}'); + cy.wait(1000); + cy.get('body').type(' '); // spacebar + cy.wait(1000); + // Try clicking in different areas + cy.get('body').click(500, 300, { force: true }); + stuckCount = 0; + } + } else { + stuckCount = 0; + lastInteractionTime = Date.now(); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop after initial delay + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); +EOF + + success "Generated test file: $test_file" +} + +# Main execution +log "Starting complete task screenshot capture for all 12 tasks..." +log "Tasks to capture: $TASKS" +log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + +# Ensure webpack server is running +if ! ensure_webpack_server; then + error "Failed to start webpack server" + exit 1 +fi + +# Create backup directory +BACKUP_DIR="all_tasks_complete_$(date +%Y%m%d_%H%M%S)" +log "Results will be backed up to: $BACKUP_DIR" + +# Process each task +task_count=0 +total_tasks=$(echo $TASKS | wc -w) + +for task in $TASKS; do + task_count=$((task_count + 1)) + log "Processing task $task_count/$total_tasks: $task" + + # Generate test + generate_complete_test "$task" + + # Run Cypress test with timeout + log "Running Cypress test for $task..." + if timeout $MAX_TIMEOUT npx cypress run --spec "cypress/e2e/${task}_complete.cy.js" --browser electron --config video=false; then + success "Cypress test completed for $task" + + # Check screenshots + screenshot_dir="cypress/screenshots/${task}_complete.cy.js" + if [[ -d "$screenshot_dir" ]]; then + screenshot_count=\$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + success "Task $task: \$screenshot_count screenshots captured" + + # Run OCR cleanup + log "Running OCR cleanup for $task..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + success "OCR cleanup completed for $task" + # Count remaining after cleanup + remaining_count=\$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + log "Task $task: \$screenshot_count โ†’ \$remaining_count screenshots after cleanup" + else + warn "OCR cleanup failed for $task, keeping all screenshots" + fi + else + error "No screenshots found for $task" + fi + else + error "Cypress test failed or timed out for $task" + fi + + # Clean up test file + rm -f "cypress/e2e/${task}_complete.cy.js" + + log "Completed task $task ($task_count/$total_tasks)" + echo "----------------------------------------" +done + +# Create comprehensive backup +log "Creating comprehensive backup..." +mkdir -p "$BACKUP_DIR" +cp -r cypress/screenshots/* "$BACKUP_DIR/" 2>/dev/null || true +cp -r cypress/videos/* "$BACKUP_DIR/" 2>/dev/null || true + +# Summary +log "All tasks completed!" +log "Backup created: $BACKUP_DIR" + +# Count total results +total_screenshots=0 +for task in $TASKS; do + screenshot_dir="cypress/screenshots/${task}_complete.cy.js" + if [[ -d "$screenshot_dir" ]]; then + count=\$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + total_screenshots=\$((total_screenshots + count)) + log "Task $task: \$count screenshots" + fi +done + +success "Total screenshots captured: \$total_screenshots" +success "All results backed up to: $BACKUP_DIR" \ No newline at end of file diff --git a/task-launcher/run_all_tasks_final.sh b/task-launcher/run_all_tasks_final.sh new file mode 100755 index 000000000..434f499ff --- /dev/null +++ b/task-launcher/run_all_tasks_final.sh @@ -0,0 +1,259 @@ +#!/bin/bash + +# Final All Tasks Screenshot Capture with OCR Filtering +set -e + +# All 12 tasks +TASKS=("memory-game" "egma-math" "matrix-reasoning" "mental-rotation" "hearts-and-flowers" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") +SERVER_PORT=8080 +BACKUP_DIR="all_tasks_final_$(date +%Y%m%d_%H%M%S)" + +echo "๐Ÿš€ Final All Tasks Screenshot Capture" +echo "๐Ÿ“Š Configuration:" +echo " - Tasks: ${#TASKS[@]}" +echo " - Complete runs with OCR filtering" +echo " - Backup directory: $BACKUP_DIR" +echo "" + +# Cleanup function +cleanup() { + echo "๐Ÿงน Cleaning up processes..." + pkill -f "webpack" 2>/dev/null || true + pkill -f "cypress" 2>/dev/null || true + lsof -ti:${SERVER_PORT} | xargs kill -9 2>/dev/null || true + sleep 3 +} + +# Start server +start_server() { + echo "๐Ÿš€ Starting webpack server..." + npx webpack serve --mode development --env dbmode=development --port ${SERVER_PORT} > webpack.log 2>&1 & + + for i in {1..30}; do + if curl -s http://localhost:${SERVER_PORT} > /dev/null 2>&1; then + echo "โœ… Server ready" + return 0 + fi + sleep 2 + done + echo "โŒ Server failed to start" + return 1 +} + +# Run single task +run_task() { + local task=$1 + echo "" + echo "๐ŸŽฏ Processing: $task" + + # Create task-specific test + cat > "cypress/e2e/final_${task}.cy.js" << 'EOF' +describe('TASK_NAME Complete Capture', () => { + it('captures complete TASK_NAME run with screenshots', () => { + cy.visit('http://localhost:8080/?task=TASK_NAME', { timeout: 60000 }); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + let counter = 0; + + // Initial screenshot + cy.screenshot(`${String(counter++).padStart(2, '0')}-start`); + cy.wait(3000); + + // Start task + cy.get('body').then($body => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-after-ok`); + } else if ($body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-after-start`); + } + }); + + // Main task loop - 25 iterations with 6-second intervals + for (let i = 0; i < 25; i++) { + cy.wait(6000); + cy.screenshot(`${String(counter++).padStart(2, '0')}-step-${i}`); + + // Check for completion + cy.get('body').then($body => { + const text = $body.text().toLowerCase(); + if (text.includes('thank you') || text.includes('complete') || + text.includes('exit') || text.includes('finished') || text.includes('done')) { + cy.screenshot(`${String(counter++).padStart(2, '0')}-completion`); + return; + } + + // Smart interactions based on task type + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Memory game blocks + else if ($body.find('.jspsych-corsi-block').length > 0) { + cy.get('.jspsych-corsi-block').then($blocks => { + const randomIndex = Math.floor(Math.random() * $blocks.length); + cy.wrap($blocks[randomIndex]).click({ force: true }); + }); + } + // Adult-reasoning multi-choice + else if ($body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then($buttons => { + const randomIndex = Math.floor(Math.random() * $buttons.length); + cy.wrap($buttons[randomIndex]).click({ force: true }); + }); + } + // Matrix reasoning answer buttons + else if ($body.find('button').length > 0) { + cy.get('button').then($buttons => { + const visibleButtons = $buttons.filter(':visible'); + if (visibleButtons.length > 0) { + // Prefer answer buttons (A, B, C, D) + const answerButtons = visibleButtons.filter(':contains("A"), :contains("B"), :contains("C"), :contains("D")'); + if (answerButtons.length > 0) { + const randomIndex = Math.floor(Math.random() * answerButtons.length); + cy.wrap(answerButtons[randomIndex]).click({ force: true }); + } else { + const randomIndex = Math.floor(Math.random() * visibleButtons.length); + cy.wrap(visibleButtons[randomIndex]).click({ force: true }); + } + } + }); + } + }); + } + + cy.screenshot(`${String(counter++).padStart(2, '0')}-final`); + }); +}); +EOF + + # Replace task name + sed -i "s/TASK_NAME/$task/g" "cypress/e2e/final_${task}.cy.js" + + # Run test + echo "๐Ÿš€ Running $task test..." + local success=false + if npx cypress run --spec "cypress/e2e/final_${task}.cy.js" --headless --config defaultCommandTimeout=30000,requestTimeout=30000,responseTimeout=30000; then + success=true + echo "โœ… $task completed successfully" + else + echo "โš ๏ธ $task had issues but may have captured screenshots" + fi + + # Check results + local dir="cypress/screenshots/final_${task}.cy.js" + if [ -d "$dir" ]; then + local count=$(ls -1 "$dir"/*.png 2>/dev/null | wc -l) + echo "๐Ÿ“ธ Captured $count screenshots for $task" + + if [ $count -gt 0 ]; then + # Show file size variety + echo "๐Ÿ“Š File sizes: $(ls -lah "$dir"/*.png | awk '{print $5}' | sort -u | tr '\n' ' ')" + + # Run OCR cleanup + echo "๐Ÿ” Running OCR cleanup for $task..." + if python3 cleanup_screenshots_ocr.py "$dir/" --execute; then + local remaining=$(ls -1 "$dir"/*.png 2>/dev/null | wc -l) + local duplicates=0 + if [ -d "$dir/duplicates_backup" ]; then + duplicates=$(ls -1 "$dir/duplicates_backup"/*.png 2>/dev/null | wc -l) + fi + echo "โœจ $task OCR complete: $remaining unique, $duplicates duplicates backed up" + else + echo "โš ๏ธ OCR cleanup failed for $task, keeping all screenshots" + fi + fi + else + echo "โŒ No screenshots found for $task" + fi + + # Copy to backup + if [ -d "$dir" ]; then + mkdir -p "$BACKUP_DIR" + cp -r "$dir" "$BACKUP_DIR/" + echo "๐Ÿ’พ Backed up $task to $BACKUP_DIR/" + fi + + # Cleanup test file + rm -f "cypress/e2e/final_${task}.cy.js" + + echo "โœ… $task processing complete" +} + +# Main execution +echo "๐Ÿงน Initial cleanup..." +cleanup + +echo "๐Ÿš€ Starting server..." +if ! start_server; then + echo "โŒ Failed to start server. Exiting." + exit 1 +fi + +echo "๐Ÿ“ Creating backup directory: $BACKUP_DIR" +mkdir -p "$BACKUP_DIR" + +# Process all tasks +total_tasks=${#TASKS[@]} +current=1 + +for task in "${TASKS[@]}"; do + echo "" + echo "๐Ÿ“ Task $current of $total_tasks: $task" + run_task "$task" + current=$((current + 1)) + + # Brief pause between tasks + sleep 3 +done + +# Final cleanup +echo "" +echo "๐Ÿงน Final cleanup..." +cleanup + +# Generate summary +echo "" +echo "๐ŸŽ‰ ALL TASKS COMPLETED!" +echo "๐Ÿ“ Complete backup: $BACKUP_DIR/" +echo "" +echo "๐Ÿ“Š FINAL SUMMARY:" +echo "================================================" + +total_unique=0 +total_duplicates=0 + +for task in "${TASKS[@]}"; do + local dir="cypress/screenshots/final_${task}.cy.js" + if [ -d "$dir" ]; then + local unique_count=$(ls -1 "$dir"/*.png 2>/dev/null | wc -l) + local duplicate_count=0 + if [ -d "$dir/duplicates_backup" ]; then + duplicate_count=$(ls -1 "$dir/duplicates_backup"/*.png 2>/dev/null | wc -l) + fi + + total_unique=$((total_unique + unique_count)) + total_duplicates=$((total_duplicates + duplicate_count)) + + printf "%-25s: %2d unique, %2d duplicates\n" "$task" "$unique_count" "$duplicate_count" + else + printf "%-25s: %s\n" "$task" "No screenshots" + fi +done + +echo "================================================" +echo "TOTALS: $total_unique unique screenshots, $total_duplicates duplicates backed up" +echo "" +echo "โœ… Complete task screenshot capture finished!" +echo "๐Ÿ“‚ All results preserved in: $BACKUP_DIR/" \ No newline at end of file diff --git a/task-launcher/run_all_tasks_fixed.sh b/task-launcher/run_all_tasks_fixed.sh new file mode 100755 index 000000000..deb6e72ce --- /dev/null +++ b/task-launcher/run_all_tasks_fixed.sh @@ -0,0 +1,500 @@ +#!/bin/bash + +# Comprehensive Task Screenshot Capture with Smart Interactions +# Fixed version with proper associative array handling + +# set -e # Disable exit on error to continue with other tasks + +# Configuration +SCREENSHOT_INTERVAL=8 # seconds between screenshots +TASK_DURATION=180 # 3 minutes per task +MAX_TIMEOUT=300 # 5 minute maximum timeout per task +BACKUP_DIR="all_tasks_fixed_$(date +%Y%m%d_%H%M%S)" + +# Initialize associative array properly +declare -A TASKS +TASKS["egma-math"]="afc_with_slider" +TASKS["matrix-reasoning"]="afc_multi_choice" +TASKS["mental-rotation"]="afc_multi_choice" +TASKS["hearts-and-flowers"]="spatial_response" +TASKS["memory-game"]="corsi_blocks" +TASKS["same-different-selection"]="binary_choice" +TASKS["trog"]="afc_multi_choice" +TASKS["vocab"]="afc_multi_choice" +TASKS["theory-of-mind"]="afc_multi_choice" +TASKS["intro"]="instruction_only" +TASKS["roar-inference"]="afc_multi_choice" +TASKS["adult-reasoning"]="afc_multi_choice" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to start webpack dev server +start_webpack_server() { + log "Starting webpack dev server on port 8080..." + + # Kill any existing processes on port 8080 + pkill -f "webpack.*8080" || true + sleep 2 + + # Start webpack dev server in background + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + log "Waiting for webpack server to start..." + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is ready!" + return 0 + fi + sleep 1 + done + + error "Webpack server failed to start" + return 1 +} + +# Function to stop webpack server +stop_webpack_server() { + if [[ -n "$WEBPACK_PID" ]]; then + log "Stopping webpack server (PID: $WEBPACK_PID)..." + kill $WEBPACK_PID 2>/dev/null || true + pkill -f "webpack.*8080" || true + sleep 2 + fi +} + +# Function to generate task-specific Cypress test +generate_task_test() { + local task_name="$1" + local interaction_strategy="$2" + local test_file="cypress/e2e/${task_name}_smart_capture.cy.js" + + log "Generating smart test for $task_name with $interaction_strategy strategy..." + + # Create the base test structure + cat > "$test_file" << 'EOF' +describe('Task Screenshot Capture', () => { + it('should capture screenshots with smart interactions', () => { + const taskName = 'TASK_NAME'; + const interactionStrategy = 'INTERACTION_STRATEGY'; + const screenshotInterval = SCREENSHOT_INTERVAL * 1000; + const maxDuration = TASK_DURATION * 1000; + let screenshotCount = 0; + + // Mock fullscreen API + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context for tasks that need it + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ connect: () => {}, start: () => {}, stop: () => {} }), + createGain: () => ({ connect: () => {}, gain: { value: 0 } }), + destination: {}, + currentTime: 0 + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Start interaction loop + const startTime = Date.now(); + + function performTaskSpecificInteraction() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Task-specific interaction logic + cy.then(() => { + switch (interactionStrategy) { + case 'afc_multi_choice': + return performAFCInteraction(); + case 'afc_with_slider': + return performMathInteraction(); + case 'corsi_blocks': + return performMemoryGameInteraction(); + case 'spatial_response': + return performSpatialInteraction(); + case 'binary_choice': + return performBinaryChoiceInteraction(); + case 'instruction_only': + return performInstructionInteraction(); + default: + return performGenericInteraction(); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + performTaskSpecificInteraction(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // AFC (Alternative Forced Choice) interaction for most tasks + function performAFCInteraction() { + // Look for multiple choice buttons (2-4 options) + cy.get('body').then($body => { + // Try different button selectors in order of preference + const selectors = [ + '#jspsych-html-multi-response-btngroup button', // Most common + '.jspsych-btn', // Standard jsPsych buttons + 'button[data-choice]', // Choice buttons + '.lev-response-row button', // Levante framework buttons + 'button:not(.replay):not(#replay-btn-revisited)', // Any button except replay + 'button' // Fallback to any button + ]; + + for (let selector of selectors) { + const buttons = $body.find(selector); + if (buttons.length >= 2 && buttons.length <= 4) { + // Found multi-choice buttons, click a random one + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + return; + } + } + + // Fallback: look for any clickable element + performGenericInteraction(); + }); + } + + // Math task interaction (includes sliders) + function performMathInteraction() { + cy.get('body').then($body => { + // Check for slider first + if ($body.find('input[type="range"]').length > 0) { + // Random position on slider + const randomValue = Math.floor(Math.random() * 100); + cy.get('input[type="range"]').first().invoke('val', randomValue).trigger('change'); + cy.wait(1000); + } + + // Then look for submit/continue buttons + performAFCInteraction(); + }); + } + + // Memory game interaction (Corsi blocks) + function performMemoryGameInteraction() { + cy.get('body').then($body => { + // Look for Corsi blocks or memory game elements + const memorySelectors = [ + '.corsi-block', + '#corsiBlock', + '.memory-block', + 'button:contains("OK")', + '.jspsych-btn' + ]; + + for (let selector of memorySelectors) { + if ($body.find(selector).length > 0) { + if (selector.includes('block')) { + // Click a random block + cy.get(selector).then($blocks => { + if ($blocks.length > 0) { + const randomIndex = Math.floor(Math.random() * $blocks.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + } + }); + } else { + cy.get(selector).first().click({ force: true }); + } + return; + } + } + + performGenericInteraction(); + }); + } + + // Spatial response interaction (hearts-and-flowers) + function performSpatialInteraction() { + cy.get('body').then($body => { + // Look for left/right response buttons + const spatialSelectors = [ + 'button[data-response="left"]', + 'button[data-response="right"]', + '.response-left', + '.response-right', + '.jspsych-btn' + ]; + + for (let selector of spatialSelectors) { + if ($body.find(selector).length > 0) { + // Randomly choose left or right + const isLeft = Math.random() < 0.5; + if (selector.includes('left') || selector.includes('right')) { + const targetSelector = isLeft ? selector.replace('right', 'left') : selector.replace('left', 'right'); + if ($body.find(targetSelector).length > 0) { + cy.get(targetSelector).first().click({ force: true }); + return; + } + } + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + }); + } + + // Binary choice interaction + function performBinaryChoiceInteraction() { + cy.get('body').then($body => { + const buttons = $body.find('button:not(.replay):not(#replay-btn-revisited)'); + if (buttons.length === 2) { + // Exactly 2 buttons, pick randomly + const randomIndex = Math.floor(Math.random() * 2); + cy.get('button:not(.replay):not(#replay-btn-revisited)').eq(randomIndex).click({ force: true }); + } else { + performAFCInteraction(); + } + }); + } + + // Instruction-only interaction + function performInstructionInteraction() { + cy.get('body').then($body => { + // Look for continue/next/OK buttons + const instructionButtons = [ + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("OK")', + 'button:contains("Start")', + '.jspsych-btn' + ]; + + for (let selector of instructionButtons) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + }); + } + + // Generic fallback interaction + function performGenericInteraction() { + cy.get('body').then($body => { + // Try to find any clickable element + const genericSelectors = [ + 'button:not(.replay):not(#replay-btn-revisited):visible', + 'input[type="submit"]:visible', + '[role="button"]:visible', + '.clickable:visible', + 'a:visible' + ]; + + for (let selector of genericSelectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + // Last resort: click somewhere in the middle of the screen + cy.get('body').click(400, 300, { force: true }); + }); + } + + // Start the interaction loop + cy.wait(2000); // Wait for initial load + performTaskSpecificInteraction(); + }); +}); +EOF + + # Replace placeholders with actual values + sed -i "s/TASK_NAME/$task_name/g" "$test_file" + sed -i "s/INTERACTION_STRATEGY/$interaction_strategy/g" "$test_file" + sed -i "s/SCREENSHOT_INTERVAL/$SCREENSHOT_INTERVAL/g" "$test_file" + sed -i "s/TASK_DURATION/$TASK_DURATION/g" "$test_file" + + success "Generated test file: $test_file" +} + +# Function to run OCR cleanup +run_ocr_cleanup() { + local task_name="$1" + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + + if [[ -d "$screenshot_dir" ]]; then + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + success "OCR cleanup completed for $task_name" + else + warn "OCR cleanup failed for $task_name, but continuing..." + fi + else + warn "No screenshots found for $task_name" + fi +} + +# Function to capture task screenshots +capture_task() { + local task_name="$1" + local interaction_strategy="$2" + + log "Starting capture for task: $task_name (strategy: $interaction_strategy)" + + # Generate task-specific test + generate_task_test "$task_name" "$interaction_strategy" + + # Run Cypress test + log "Running Cypress test for $task_name..." + if timeout $MAX_TIMEOUT npx cypress run --spec "cypress/e2e/${task_name}_smart_capture.cy.js" --browser chrome --headless; then + success "Cypress test completed for $task_name" + + # Run OCR cleanup + run_ocr_cleanup "$task_name" + + # Count results + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + if [[ -d "$screenshot_dir" ]]; then + local total_screenshots=$(find "$screenshot_dir" -name "*.png" | wc -l) + local unique_screenshots=$(find "$screenshot_dir" -maxdepth 1 -name "*.png" | wc -l) + local duplicates_backup=$(find "$screenshot_dir/duplicates_backup" -name "*.png" 2>/dev/null | wc -l || echo 0) + + success "Task $task_name: $total_screenshots total โ†’ $unique_screenshots unique (${duplicates_backup} duplicates moved to backup)" + fi + else + error "Cypress test failed or timed out for $task_name" + return 1 + fi + + # Cleanup test file + rm -f "cypress/e2e/${task_name}_smart_capture.cy.js" + + return 0 +} + +# Main execution +main() { + log "Starting comprehensive task screenshot capture with smart interactions" + log "Total tasks defined: ${#TASKS[@]}" + + # Print all tasks for verification + log "All tasks to process:" + for task in "${!TASKS[@]}"; do + log " - $task (${TASKS[$task]})" + done + + log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + + # Start webpack server + if ! start_webpack_server; then + error "Failed to start webpack server" + exit 1 + fi + + # Ensure cleanup on exit + trap 'stop_webpack_server; log "Cleanup completed"' EXIT + + # Create backup directory + mkdir -p "$BACKUP_DIR" + + local successful_tasks=0 + local failed_tasks=0 + local total_screenshots=0 + local total_unique=0 + local task_count=0 + + # Process each task + for task_name in "${!TASKS[@]}"; do + local interaction_strategy="${TASKS[$task_name]}" + ((task_count++)) + + log "Processing task $task_count/${#TASKS[@]}: $task_name" + + if capture_task "$task_name" "$interaction_strategy"; then + ((successful_tasks++)) + + # Move results to backup directory + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + if [[ -d "$screenshot_dir" ]]; then + cp -r "$screenshot_dir" "$BACKUP_DIR/" + + # Count screenshots + local task_total=$(find "$screenshot_dir" -name "*.png" | wc -l) + local task_unique=$(find "$screenshot_dir" -maxdepth 1 -name "*.png" | wc -l) + total_screenshots=$((total_screenshots + task_total)) + total_unique=$((total_unique + task_unique)) + fi + else + ((failed_tasks++)) + warn "Task $task_name failed, continuing with next task..." + fi + + # Brief pause between tasks + sleep 2 + done + + # Final summary + log "=== FINAL SUMMARY ===" + success "Successful tasks: $successful_tasks/${#TASKS[@]}" + if [[ $failed_tasks -gt 0 ]]; then + warn "Failed tasks: $failed_tasks" + fi + success "Total screenshots: $total_screenshots" + success "Unique screenshots (after OCR): $total_unique" + if [[ $total_screenshots -gt 0 ]]; then + success "Reduction: $(( (total_screenshots - total_unique) * 100 / total_screenshots ))%" + fi + success "Results backed up to: $BACKUP_DIR" + + log "Task capture completed!" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/run_all_tasks_robust.sh b/task-launcher/run_all_tasks_robust.sh new file mode 100755 index 000000000..30094de11 --- /dev/null +++ b/task-launcher/run_all_tasks_robust.sh @@ -0,0 +1,271 @@ +#!/bin/bash +# Robust comprehensive script to capture screenshots for all available tasks + +echo "๐ŸŽฏ ROBUST COMPREHENSIVE TASK SCREENSHOT CAPTURE" +echo "==================================================" + +# Create timestamped backup directory for all results +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_DIR="all_task_screenshots_$TIMESTAMP" +echo "๐Ÿ“ Results will be preserved in: $BACKUP_DIR" + +# Task list from taskConfig.ts +TASKS=( + "egma-math" + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Global variables +SERVER_PID="" +SERVER_ATTEMPTS=0 +MAX_SERVER_ATTEMPTS=3 + +# Function to kill any existing servers +cleanup_servers() { + echo "๐Ÿงน Cleaning up existing servers..." + pkill -f "webpack serve" 2>/dev/null || true + pkill -f "port 8080" 2>/dev/null || true + lsof -ti:8080 | xargs kill -9 2>/dev/null || true + sleep 3 +} + +# Function to start webpack dev server with retries +start_dev_server() { + local attempt=1 + + while [ $attempt -le $MAX_SERVER_ATTEMPTS ]; do + echo "๐Ÿš€ Starting webpack dev server (attempt $attempt/$MAX_SERVER_ATTEMPTS)..." + + # Clean up any existing servers + cleanup_servers + + # Start new server + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + SERVER_PID=$! + + # Wait for server to start + echo "โณ Waiting for dev server to start..." + sleep 20 + + # Check if server is responding + if curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โœ… Dev server started successfully on port 8080" + return 0 + else + echo "โŒ Dev server failed to start (attempt $attempt)" + kill $SERVER_PID 2>/dev/null || true + ((attempt++)) + sleep 5 + fi + done + + echo "๐Ÿ’ฅ Failed to start dev server after $MAX_SERVER_ATTEMPTS attempts" + return 1 +} + +# Function to check if server is still running +check_server() { + if ! curl -s http://localhost:8080 > /dev/null 2>&1; then + echo "โš ๏ธ Server appears to be down, attempting restart..." + start_dev_server + return $? + fi + return 0 +} + +# Function to create and run test for a task +run_task_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name//-/_}_complete.cy.js" + + echo "" + echo "==================== ${task_name^^} ====================" + echo "๐Ÿ“ธ Running screenshots for: $task_name" + + # Check server before starting test + if ! check_server; then + echo "โŒ Cannot start test - server is not available" + return 1 + fi + + # Create Cypress test file + cat > "$test_file" << EOF +const ${task_name//-/_}_url = 'http://localhost:8080/?task=${task_name}'; + +describe('${task_name^} Complete Run', () => { + let screenshotCounter = 1; + + function takeScreenshot(description) { + cy.screenshot(\`\${screenshotCounter.toString().padStart(3, '0')}-\${description}\`); + screenshotCounter++; + } + + it('runs complete ${task_name} with screenshots', () => { + // Visit with fullscreen mocking and extended timeout + cy.visit(${task_name//-/_}_url, { + timeout: 60000, + onBeforeLoad: (win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 8 minutes with screenshots every 10 seconds + const totalDuration = 8 * 60 * 1000; // 8 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) { + cy.wait(screenshotInterval); + takeScreenshot(\`interval-\${i.toString().padStart(2, '0')}\`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then((\$body) => { + // Click OK buttons if they exist + if (\$body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } + // Click Continue buttons if they exist + if (\$body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } + // Click Next buttons if they exist + if (\$body.find('button:contains("Next")').length > 0) { + cy.get('button:contains("Next")').first().click({ force: true }); + } + // Click Start buttons if they exist + if (\$body.find('button:contains("Start")').length > 0) { + cy.get('button:contains("Start")').first().click({ force: true }); + } + // Click any visible buttons as fallback + if (\$body.find('button:visible').length > 0) { + cy.get('button:visible').first().click({ force: true }); + } + }); + } + + // Final screenshot + takeScreenshot('final-state'); + }); +}); +EOF + + # Run Cypress test with retries + local test_attempts=0 + local max_test_attempts=2 + + while [ $test_attempts -lt $max_test_attempts ]; do + echo "๐Ÿƒ Running Cypress test (attempt $((test_attempts + 1))/$max_test_attempts)..." + + if timeout 600 npx cypress run --spec "$test_file" --record false; then + echo "โœ… $task_name screenshots completed" + + # Run OCR cleanup (preserves duplicates in backup folder) + local screenshot_dir="cypress/screenshots/${task_name//-/_}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + echo "๐Ÿ” Running OCR cleanup for $task_name (duplicates preserved in backup)..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + echo "โœ… $task_name cleanup complete - unique screenshots kept, duplicates backed up" + else + echo "โš ๏ธ $task_name cleanup had issues" + fi + else + echo "โš ๏ธ No screenshots found for $task_name" + fi + return 0 + else + echo "โš ๏ธ $task_name test failed (attempt $((test_attempts + 1)))" + ((test_attempts++)) + + # Check if server is still running after failure + if [ $test_attempts -lt $max_test_attempts ]; then + echo "๐Ÿ”„ Checking server status before retry..." + check_server + sleep 5 + fi + fi + done + + echo "โŒ $task_name test failed after $max_test_attempts attempts" + return 1 +} + +# Cleanup function +cleanup() { + echo "" + echo "๐Ÿ›‘ Stopping dev server..." + kill $SERVER_PID 2>/dev/null || true + cleanup_servers +} + +# Set trap to cleanup on exit +trap cleanup EXIT + +# Start the dev server +if ! start_dev_server; then + echo "๐Ÿ’ฅ Cannot start dev server. Exiting." + exit 1 +fi + +# Run tests for all tasks +successful=0 +failed=0 + +for task in "${TASKS[@]}"; do + if run_task_test "$task"; then + ((successful++)) + else + ((failed++)) + fi + + # Small delay between tasks + sleep 5 + + # Periodic server health check + if ! check_server; then + echo "๐Ÿ’ฅ Server health check failed. Stopping." + break + fi +done + +# Create comprehensive backup of all results +echo "" +echo "๐Ÿ“ฆ Creating comprehensive backup..." +if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots "$BACKUP_DIR" + echo "โœ… All screenshots backed up to: $BACKUP_DIR" +else + echo "โš ๏ธ No screenshots directory found" +fi + +# Final summary +echo "" +echo "==================================================" +echo "๐Ÿ“Š FINAL SUMMARY REPORT" +echo "==================================================" +echo "โœ… Successful tasks: $successful" +echo "โŒ Failed tasks: $failed" +echo "๐Ÿ“ Complete backup: $BACKUP_DIR" +echo "๐ŸŽ‰ Robust task screenshot capture complete!" + +exit 0 \ No newline at end of file diff --git a/task-launcher/run_all_tasks_screenshots.py b/task-launcher/run_all_tasks_screenshots.py new file mode 100644 index 000000000..6e4ea09dc --- /dev/null +++ b/task-launcher/run_all_tasks_screenshots.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python3 +""" +Comprehensive script to capture screenshots for all available tasks +and process them with OCR-based cleanup. +""" + +import os +import sys +import subprocess +import time +import json +from pathlib import Path +from typing import List, Dict + +# Task mapping from taskConfig.ts to URL parameters +TASKS = { + 'egma-math': 'egmaMath', + 'matrix-reasoning': 'matrixReasoning', + 'mental-rotation': 'mentalRotation', + 'hearts-and-flowers': 'heartsAndFlowers', + 'memory-game': 'memoryGame', + 'same-different-selection': 'sameDifferentSelection', + 'trog': 'trog', + 'vocab': 'vocab', + 'theory-of-mind': 'theoryOfMind', + 'intro': 'intro', + 'roar-inference': 'roarInference', + 'adult-reasoning': 'adultReasoning' +} + +def create_task_test(task_url_param: str, task_name: str) -> str: + """Create a Cypress test file for a specific task.""" + test_content = f'''const {task_name.replace('-', '_')}_url = 'http://localhost:8080/?task={task_url_param}'; + +describe('{task_name.title().replace("-", " ")} Complete Run', () => {{ + let screenshotCounter = 1; + + function takeScreenshot(description) {{ + cy.screenshot(`${{screenshotCounter.toString().padStart(3, '0')}}-${{description}}`); + screenshotCounter++; + }} + + it('runs complete {task_name.replace("-", " ")} with screenshots', () => {{ + // Visit with fullscreen mocking and extended timeout + cy.visit({task_name.replace('-', '_')}_url, {{ + timeout: 60000, + onBeforeLoad: (win) => {{ + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', {{ + get: () => win.document.documentElement + }}); + Object.defineProperty(win.document, 'fullscreenEnabled', {{ + get: () => true + }}); + }} + }}); + + // Initial screenshot + takeScreenshot('initial-load'); + + // Run for 4 minutes with screenshots every 10 seconds + const totalDuration = 4 * 60 * 1000; // 4 minutes + const screenshotInterval = 10 * 1000; // 10 seconds + const numScreenshots = Math.floor(totalDuration / screenshotInterval); + + // Take screenshots at regular intervals + for (let i = 1; i <= numScreenshots; i++) {{ + cy.wait(screenshotInterval); + takeScreenshot(`interval-${{i.toString().padStart(2, '0')}}`); + + // Try to interact with common elements (non-blocking) + cy.get('body').then(($body) => {{ + // Click OK buttons if they exist + if ($body.find('button:contains("OK")').length > 0) {{ + cy.get('button:contains("OK")').first().click({{ force: true }}); + }} + // Click Continue buttons if they exist + if ($body.find('button:contains("Continue")').length > 0) {{ + cy.get('button:contains("Continue")').first().click({{ force: true }}); + }} + // Click Next buttons if they exist + if ($body.find('button:contains("Next")').length > 0) {{ + cy.get('button:contains("Next")').first().click({{ force: true }}); + }} + // Click Start buttons if they exist + if ($body.find('button:contains("Start")').length > 0) {{ + cy.get('button:contains("Start")').first().click({{ force: true }}); + }} + // Click any visible buttons as fallback + if ($body.find('button:visible').length > 0) {{ + cy.get('button:visible').first().click({{ force: true }}); + }} + }}); + }} + + // Final screenshot + takeScreenshot('final-state'); + }}); +}}); +''' + return test_content + +def start_dev_server(): + """Start the webpack dev server.""" + print("๐Ÿš€ Starting webpack dev server...") + + # Kill any existing processes on port 8080 + try: + subprocess.run(['pkill', '-f', 'webpack serve'], capture_output=True) + subprocess.run(['pkill', '-f', 'port 8080'], capture_output=True) + time.sleep(2) + except: + pass + + # Start dev server in background + server_process = subprocess.Popen([ + 'npx', 'webpack', 'serve', + '--mode', 'development', + '--env', 'dbmode=development', + '--port', '8080' + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + # Wait for server to start + print("โณ Waiting for dev server to start...") + time.sleep(15) + + return server_process + +def run_task_test(task_url_param: str, task_name: str) -> bool: + """Run Cypress test for a specific task.""" + print(f"\n๐Ÿ“ธ Running screenshots for: {task_name}") + + # Create test file + test_filename = f"cypress/e2e/{task_name.replace('-', '_')}_complete.cy.js" + test_content = create_task_test(task_url_param, task_name) + + with open(test_filename, 'w') as f: + f.write(test_content) + + # Run Cypress test + try: + result = subprocess.run([ + 'npx', 'cypress', 'run', + '--spec', test_filename, + '--record', 'false' + ], capture_output=True, text=True, timeout=300) # 5 minute timeout + + if result.returncode == 0: + print(f"โœ… {task_name} screenshots completed successfully") + return True + else: + print(f"โš ๏ธ {task_name} test completed with warnings") + print(f" Output: {result.stdout[-200:]}") # Last 200 chars + return True # Still consider it successful for screenshots + + except subprocess.TimeoutExpired: + print(f"โฐ {task_name} test timed out after 5 minutes") + return False + except Exception as e: + print(f"โŒ {task_name} test failed: {e}") + return False + +def cleanup_screenshots(task_name: str) -> Dict: + """Run OCR cleanup on screenshots for a task.""" + screenshot_dir = f"cypress/screenshots/{task_name.replace('-', '_')}_complete.cy.js" + + if not os.path.exists(screenshot_dir): + print(f"โš ๏ธ No screenshots found for {task_name}") + return {"status": "no_screenshots"} + + print(f"๐Ÿ” Running OCR cleanup for {task_name}...") + + try: + # Run OCR cleanup + result = subprocess.run([ + 'python3', 'cleanup_screenshots_ocr.py', + screenshot_dir, '--execute' + ], capture_output=True, text=True) + + if result.returncode == 0: + # Parse the output to get statistics + output_lines = result.stdout.strip().split('\n') + stats = {} + for line in output_lines: + if 'Total screenshots:' in line: + stats['total'] = int(line.split(':')[1].strip()) + elif 'Unique groups:' in line: + stats['unique'] = int(line.split(':')[1].strip()) + elif 'Duplicates removed:' in line: + stats['removed'] = int(line.split(':')[1].strip()) + + print(f"โœ… {task_name} cleanup complete: {stats.get('unique', '?')} unique from {stats.get('total', '?')} total") + return {"status": "success", "stats": stats} + else: + print(f"โš ๏ธ {task_name} cleanup had issues: {result.stderr}") + return {"status": "partial", "error": result.stderr} + + except Exception as e: + print(f"โŒ {task_name} cleanup failed: {e}") + return {"status": "failed", "error": str(e)} + +def main(): + """Main execution function.""" + print("๐ŸŽฏ COMPREHENSIVE TASK SCREENSHOT CAPTURE") + print("=" * 50) + + # Change to task-launcher directory + os.chdir('task-launcher') + + # Start dev server + server_process = start_dev_server() + + results = {} + successful_tasks = [] + failed_tasks = [] + + try: + # Process each task + for task_url_param, task_config_name in TASKS.items(): + print(f"\n{'='*20} {task_url_param.upper()} {'='*20}") + + # Run screenshot capture + success = run_task_test(task_url_param, task_url_param) + + if success: + # Run OCR cleanup + cleanup_result = cleanup_screenshots(task_url_param) + results[task_url_param] = { + "screenshots": "success", + "cleanup": cleanup_result + } + successful_tasks.append(task_url_param) + else: + results[task_url_param] = { + "screenshots": "failed", + "cleanup": {"status": "skipped"} + } + failed_tasks.append(task_url_param) + + # Small delay between tasks + time.sleep(3) + + finally: + # Stop dev server + print("\n๐Ÿ›‘ Stopping dev server...") + server_process.terminate() + try: + server_process.wait(timeout=5) + except subprocess.TimeoutExpired: + server_process.kill() + + # Generate summary report + print(f"\n{'='*50}") + print("๐Ÿ“Š FINAL SUMMARY REPORT") + print(f"{'='*50}") + + print(f"โœ… Successful tasks ({len(successful_tasks)}):") + for task in successful_tasks: + stats = results[task].get("cleanup", {}).get("stats", {}) + unique = stats.get("unique", "?") + total = stats.get("total", "?") + print(f" โ€ข {task}: {unique} unique screenshots from {total} total") + + if failed_tasks: + print(f"\nโŒ Failed tasks ({len(failed_tasks)}):") + for task in failed_tasks: + print(f" โ€ข {task}") + + # Save detailed results + with open('task_screenshot_results.json', 'w') as f: + json.dump(results, f, indent=2) + + print(f"\n๐Ÿ“„ Detailed results saved to: task_screenshot_results.json") + print(f"๐ŸŽ‰ Task screenshot capture complete!") + + return len(successful_tasks), len(failed_tasks) + +if __name__ == "__main__": + successful, failed = main() + sys.exit(0 if failed == 0 else 1) \ No newline at end of file diff --git a/task-launcher/run_all_tasks_simple.sh b/task-launcher/run_all_tasks_simple.sh new file mode 100755 index 000000000..8a4979c55 --- /dev/null +++ b/task-launcher/run_all_tasks_simple.sh @@ -0,0 +1,263 @@ +#!/bin/bash + +# Comprehensive Task Screenshot Capture - Simplified Version +# Captures screenshots from all 12 tasks with OCR cleanup + +set -e + +# Configuration +TASKS=("hearts-and-flowers" "egma-math" "matrix-reasoning" "mental-rotation" "memory-game" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") +SCREENSHOT_INTERVAL=8 # seconds between screenshots +TASK_DURATION=180 # 3 minutes per task +WEBPACK_PORT=8080 +BASE_URL="http://localhost:${WEBPACK_PORT}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to check if server is ready +wait_for_server() { + local url=$1 + local max_attempts=30 + local attempt=1 + + log "Waiting for server to be ready at $url..." + + while [ $attempt -le $max_attempts ]; do + if curl -s "$url" > /dev/null 2>&1; then + success "Server is ready!" + return 0 + fi + + echo -n "." + sleep 2 + ((attempt++)) + done + + error "Server failed to start after $max_attempts attempts" + return 1 +} + +# Function to generate Cypress test for a task +generate_cypress_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name}_simple_capture.cy.js" + + cat > "$test_file" << EOF +describe('Task Screenshot Capture - ${task_name}', () => { + it('should capture screenshots with smart interactions', () => { + // Mock fullscreen API to prevent errors + cy.visit('${BASE_URL}/?task=${task_name}', { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + } + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Wait for initial load + cy.wait(3000); + + // Capture screenshots at regular intervals with interactions + for (let i = 0; i < Math.floor(${TASK_DURATION} / ${SCREENSHOT_INTERVAL}); i++) { + // Smart interaction logic + cy.get('body').then((\$body) => { + // Try to click various common interactive elements + const selectors = [ + 'button:contains("OK")', + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("Start")', + 'button:contains("Begin")', + '.jspsych-btn', + 'button[type="button"]', + '.btn', + '[role="button"]', + 'input[type="button"]', + 'input[type="submit"]', + '.corsi-block', + '.afc-stimulus button', + '.response-button', + 'button' + ]; + + // Try each selector until we find clickable elements + let clicked = false; + for (const selector of selectors) { + if (\$body.find(selector).length > 0) { + cy.get(selector).first().then((\$el) => { + if (\$el.is(':visible') && !clicked) { + cy.wrap(\$el).click({ force: true }); + clicked = true; + } + }); + break; + } + } + + // If no specific buttons, try random clicking + if (!clicked) { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Wait for the interval + cy.wait(${SCREENSHOT_INTERVAL} * 1000); + + // Take screenshot + cy.screenshot(\`\${String(i + 1).padStart(2, '0')}-step-\${i}\`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); +EOF + + success "Generated test file: $test_file" +} + +# Function to run OCR cleanup +run_ocr_cleanup() { + local task_name=$1 + local screenshot_dir="cypress/screenshots/${task_name}_simple_capture.cy.js" + + if [ -d "$screenshot_dir" ]; then + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + success "OCR cleanup completed for $task_name" + + # Count results + local total_screenshots=$(find "$screenshot_dir" -name "*.png" -not -path "*/duplicates_backup/*" | wc -l) + local backup_screenshots=0 + if [ -d "$screenshot_dir/duplicates_backup" ]; then + backup_screenshots=$(find "$screenshot_dir/duplicates_backup" -name "*.png" | wc -l) + fi + local original_total=$((total_screenshots + backup_screenshots)) + + success "Task $task_name: $original_total total โ†’ $total_screenshots unique ($backup_screenshots duplicates moved to backup)" + else + warning "OCR cleanup failed for $task_name, keeping all screenshots" + fi + else + warning "No screenshots found for $task_name" + fi +} + +# Function to create timestamped backup +create_backup() { + local timestamp=$(date +%Y%m%d_%H%M%S) + local backup_dir="all_tasks_simple_$timestamp" + + log "Creating comprehensive backup: $backup_dir" + mkdir -p "$backup_dir" + + # Copy all task screenshot directories + if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots/* "$backup_dir/" 2>/dev/null || true + fi + + success "Backup created: $backup_dir" +} + +# Main execution +main() { + log "Starting comprehensive task screenshot capture (simplified version)" + log "Tasks to capture: ${TASKS[*]}" + log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + + # Start webpack dev server + log "Starting webpack dev server on port $WEBPACK_PORT..." + npx webpack serve --mode development --env dbmode=development --port $WEBPACK_PORT > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + if ! wait_for_server "$BASE_URL"; then + error "Failed to start webpack server" + kill $WEBPACK_PID 2>/dev/null || true + exit 1 + fi + + # Process each task + local task_count=0 + local total_tasks=${#TASKS[@]} + + for task in "${TASKS[@]}"; do + ((task_count++)) + log "Processing task $task_count/$total_tasks: $task" + + # Generate and run Cypress test + log "Starting capture for task: $task" + generate_cypress_test "$task" + + log "Running Cypress test for $task..." + if timeout 300 npx cypress run --spec "cypress/e2e/${task}_simple_capture.cy.js" --browser chrome --headless; then + success "Cypress test completed for $task" + + # Run OCR cleanup + run_ocr_cleanup "$task" + else + error "Cypress test failed or timed out for $task" + fi + + # Small delay between tasks + sleep 5 + done + + # Create comprehensive backup + create_backup + + # Stop webpack server + log "Stopping webpack server (PID: $WEBPACK_PID)..." + kill $WEBPACK_PID 2>/dev/null || true + sleep 2 + + success "All tasks completed!" + log "Check the timestamped backup directory for all results" +} + +# Cleanup function +cleanup() { + log "Cleanup initiated..." + if [ ! -z "$WEBPACK_PID" ]; then + kill $WEBPACK_PID 2>/dev/null || true + fi + pkill -f "webpack serve" 2>/dev/null || true + log "Cleanup completed" +} + +# Set trap for cleanup +trap cleanup EXIT INT TERM + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/run_all_tasks_smart.sh b/task-launcher/run_all_tasks_smart.sh new file mode 100755 index 000000000..acd58c810 --- /dev/null +++ b/task-launcher/run_all_tasks_smart.sh @@ -0,0 +1,511 @@ +#!/bin/bash + +# Comprehensive Task Screenshot Capture with Smart Interactions +# Based on task timeline analysis for optimal interaction strategies + +set -e + +# Configuration +SCREENSHOT_INTERVAL=8 # seconds between screenshots +TASK_DURATION=180 # 3 minutes per task (enough for most to complete) +MAX_TIMEOUT=300 # 5 minute maximum timeout per task +BACKUP_DIR="all_tasks_smart_$(date +%Y%m%d_%H%M%S)" + +# Task list with their specific interaction strategies +declare -A TASKS=( + ["egma-math"]="afc_with_slider" + ["matrix-reasoning"]="afc_multi_choice" + ["mental-rotation"]="afc_multi_choice" + ["hearts-and-flowers"]="spatial_response" + ["memory-game"]="corsi_blocks" + ["same-different-selection"]="binary_choice" + ["trog"]="afc_multi_choice" + ["vocab"]="afc_multi_choice" + ["theory-of-mind"]="afc_multi_choice" + ["intro"]="instruction_only" + ["roar-inference"]="afc_multi_choice" + ["adult-reasoning"]="afc_multi_choice" +) + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to start webpack dev server +start_webpack_server() { + log "Starting webpack dev server on port 8080..." + # We're already in task-launcher directory + + # Kill any existing processes on port 8080 + pkill -f "webpack.*8080" || true + sleep 2 + + # Start webpack dev server in background + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + log "Waiting for webpack server to start..." + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is ready!" + return 0 + fi + sleep 1 + done + + error "Webpack server failed to start" + return 1 +} + +# Function to stop webpack server +stop_webpack_server() { + if [[ -n "$WEBPACK_PID" ]]; then + log "Stopping webpack server (PID: $WEBPACK_PID)..." + kill $WEBPACK_PID 2>/dev/null || true + pkill -f "webpack.*8080" || true + sleep 2 + fi +} + +# Function to generate task-specific Cypress test +generate_task_test() { + local task_name="$1" + local interaction_strategy="$2" + local test_file="cypress/e2e/${task_name}_smart_capture.cy.js" + + log "Generating smart test for $task_name with $interaction_strategy strategy..." + + # Create the base test structure + cat > "$test_file" << 'EOF' +describe('Task Screenshot Capture', () => { + it('should capture screenshots with smart interactions', () => { + const taskName = 'TASK_NAME'; + const interactionStrategy = 'INTERACTION_STRATEGY'; + const screenshotInterval = SCREENSHOT_INTERVAL * 1000; + const maxDuration = TASK_DURATION * 1000; + let screenshotCount = 0; + + // Mock fullscreen API + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context for tasks that need it + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ connect: () => {}, start: () => {}, stop: () => {} }), + createGain: () => ({ connect: () => {}, gain: { value: 0 } }), + destination: {}, + currentTime: 0 + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(`00-start`, { capture: 'viewport' }); + screenshotCount++; + + // Start interaction loop + const startTime = Date.now(); + + function performTaskSpecificInteraction() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-step-${screenshotCount - 1}`, { capture: 'viewport' }); + screenshotCount++; + + // Task-specific interaction logic + cy.then(() => { + switch (interactionStrategy) { + case 'afc_multi_choice': + return performAFCInteraction(); + case 'afc_with_slider': + return performMathInteraction(); + case 'corsi_blocks': + return performMemoryGameInteraction(); + case 'spatial_response': + return performSpatialInteraction(); + case 'binary_choice': + return performBinaryChoiceInteraction(); + case 'instruction_only': + return performInstructionInteraction(); + default: + return performGenericInteraction(); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + performTaskSpecificInteraction(); + } else { + cy.screenshot(`${String(screenshotCount).padStart(2, '0')}-final`, { capture: 'viewport' }); + } + }); + } + + // AFC (Alternative Forced Choice) interaction for most tasks + function performAFCInteraction() { + // Look for multiple choice buttons (2-4 options) + cy.get('body').then($body => { + // Try different button selectors in order of preference + const selectors = [ + '#jspsych-html-multi-response-btngroup button', // Most common + '.jspsych-btn', // Standard jsPsych buttons + 'button[data-choice]', // Choice buttons + '.lev-response-row button', // Levante framework buttons + 'button:not(.replay):not(#replay-btn-revisited)', // Any button except replay + 'button' // Fallback to any button + ]; + + for (let selector of selectors) { + const buttons = $body.find(selector); + if (buttons.length >= 2 && buttons.length <= 4) { + // Found multi-choice buttons, click a random one + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + return; + } + } + + // Fallback: look for any clickable element + performGenericInteraction(); + }); + } + + // Math task interaction (includes sliders) + function performMathInteraction() { + cy.get('body').then($body => { + // Check for slider first + if ($body.find('input[type="range"], .slider').length > 0) { + cy.get('input[type="range"], .slider').first().then($slider => { + const min = $slider.attr('min') || 0; + const max = $slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + min; + cy.wrap($slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + + // Look for submit/continue button after slider + cy.wait(500); + cy.get('button').contains(/continue|submit|next|ok/i).click({ force: true }); + } else { + // No slider, use AFC interaction + performAFCInteraction(); + } + }); + } + + // Memory game interaction (Corsi blocks) + function performMemoryGameInteraction() { + cy.get('body').then($body => { + // Look for Corsi blocks or memory game elements + const corsiSelectors = [ + '.corsi-block', + '.memory-block', + '[data-block]', + '.block', + 'div[style*="background-color"]:not(.instructions)' + ]; + + let foundBlocks = false; + for (let selector of corsiSelectors) { + const blocks = $body.find(selector); + if (blocks.length > 0) { + // Click a random block + const randomIndex = Math.floor(Math.random() * blocks.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + foundBlocks = true; + break; + } + } + + if (!foundBlocks) { + // Look for OK/Continue buttons (common in memory game instructions) + const buttonSelectors = [ + 'button:contains("OK")', + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("Start")', + '.jspsych-btn' + ]; + + for (let selector of buttonSelectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + } + }); + } + + // Spatial response (Hearts and Flowers) + function performSpatialInteraction() { + cy.get('body').then($body => { + // Look for directional buttons or spatial elements + const spatialSelectors = [ + 'button[data-direction]', + '.direction-button', + 'button:contains("โ†")', + 'button:contains("โ†’")', + 'button:contains("โ†‘")', + 'button:contains("โ†“")', + '.spatial-response button' + ]; + + let foundSpatial = false; + for (let selector of spatialSelectors) { + if ($body.find(selector).length > 0) { + const buttons = $body.find(selector); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get(selector).eq(randomIndex).click({ force: true }); + foundSpatial = true; + break; + } + } + + if (!foundSpatial) { + performAFCInteraction(); + } + }); + } + + // Binary choice interaction + function performBinaryChoiceInteraction() { + cy.get('body').then($body => { + const buttons = $body.find('button:not(.replay):not(#replay-btn-revisited)'); + if (buttons.length === 2) { + // Exactly 2 buttons, pick randomly + const randomIndex = Math.floor(Math.random() * 2); + cy.get('button:not(.replay):not(#replay-btn-revisited)').eq(randomIndex).click({ force: true }); + } else { + performAFCInteraction(); + } + }); + } + + // Instruction-only interaction + function performInstructionInteraction() { + cy.get('body').then($body => { + // Look for continue/next/OK buttons + const instructionButtons = [ + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("OK")', + 'button:contains("Start")', + '.jspsych-btn' + ]; + + for (let selector of instructionButtons) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + performGenericInteraction(); + }); + } + + // Generic fallback interaction + function performGenericInteraction() { + cy.get('body').then($body => { + // Try to find any clickable element + const genericSelectors = [ + 'button:not(.replay):not(#replay-btn-revisited):visible', + 'input[type="submit"]:visible', + '[role="button"]:visible', + '.clickable:visible', + 'a:visible' + ]; + + for (let selector of genericSelectors) { + if ($body.find(selector).length > 0) { + cy.get(selector).first().click({ force: true }); + return; + } + } + + // Last resort: click somewhere in the middle of the screen + cy.get('body').click(400, 300, { force: true }); + }); + } + + // Start the interaction loop + cy.wait(2000); // Wait for initial load + performTaskSpecificInteraction(); + }); +}); +EOF + + # Replace placeholders with actual values + sed -i "s/TASK_NAME/$task_name/g" "$test_file" + sed -i "s/INTERACTION_STRATEGY/$interaction_strategy/g" "$test_file" + sed -i "s/SCREENSHOT_INTERVAL/$SCREENSHOT_INTERVAL/g" "$test_file" + sed -i "s/TASK_DURATION/$TASK_DURATION/g" "$test_file" + + success "Generated test file: $test_file" +} + +# Function to run OCR cleanup +run_ocr_cleanup() { + local task_name="$1" + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + + if [[ -d "$screenshot_dir" ]]; then + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir"; then + success "OCR cleanup completed for $task_name" + else + warn "OCR cleanup failed for $task_name, but continuing..." + fi + else + warn "No screenshots found for $task_name" + fi +} + +# Function to capture task screenshots +capture_task() { + local task_name="$1" + local interaction_strategy="$2" + + log "Starting capture for task: $task_name (strategy: $interaction_strategy)" + + # Generate task-specific test + generate_task_test "$task_name" "$interaction_strategy" + + # Run Cypress test + log "Running Cypress test for $task_name..." + if timeout $MAX_TIMEOUT npx cypress run --spec "cypress/e2e/${task_name}_smart_capture.cy.js" --browser chrome --headless; then + success "Cypress test completed for $task_name" + + # Run OCR cleanup + run_ocr_cleanup "$task_name" + + # Count results + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + if [[ -d "$screenshot_dir" ]]; then + local total_screenshots=$(find "$screenshot_dir" -name "*.png" | wc -l) + local unique_screenshots=$(find "$screenshot_dir" -maxdepth 1 -name "*.png" | wc -l) + local duplicates_backup=$(find "$screenshot_dir/duplicates_backup" -name "*.png" 2>/dev/null | wc -l || echo 0) + + success "Task $task_name: $total_screenshots total โ†’ $unique_screenshots unique (${duplicates_backup} duplicates moved to backup)" + fi + else + error "Cypress test failed or timed out for $task_name" + return 1 + fi + + # Cleanup test file + rm -f "cypress/e2e/${task_name}_smart_capture.cy.js" + + return 0 +} + +# Main execution +main() { + log "Starting comprehensive task screenshot capture with smart interactions" + log "Total tasks defined: ${#TASKS[@]}" + log "All task keys: ${!TASKS[@]}" + log "All task values: ${TASKS[@]}" + log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + + # Start webpack server + if ! start_webpack_server; then + error "Failed to start webpack server" + exit 1 + fi + + # Ensure cleanup on exit + trap 'stop_webpack_server; log "Cleanup completed"' EXIT + + # Create backup directory + mkdir -p "$BACKUP_DIR" + + local successful_tasks=0 + local failed_tasks=0 + local total_screenshots=0 + local total_unique=0 + + # Process each task + for task_name in "${!TASKS[@]}"; do + local interaction_strategy="${TASKS[$task_name]}" + + log "Processing task $((successful_tasks + failed_tasks + 1))/${#TASKS[@]}: $task_name" + + if capture_task "$task_name" "$interaction_strategy"; then + ((successful_tasks++)) + + # Move results to backup directory + local screenshot_dir="cypress/screenshots/${task_name}_smart_capture.cy.js" + if [[ -d "$screenshot_dir" ]]; then + cp -r "$screenshot_dir" "$BACKUP_DIR/" + + # Count screenshots + local task_total=$(find "$screenshot_dir" -name "*.png" | wc -l) + local task_unique=$(find "$screenshot_dir" -maxdepth 1 -name "*.png" | wc -l) + total_screenshots=$((total_screenshots + task_total)) + total_unique=$((total_unique + task_unique)) + fi + else + ((failed_tasks++)) + warn "Task $task_name failed, continuing with next task..." + fi + + # Brief pause between tasks + sleep 2 + done + + # Final summary + log "=== FINAL SUMMARY ===" + success "Successful tasks: $successful_tasks/${#TASKS[@]}" + if [[ $failed_tasks -gt 0 ]]; then + warn "Failed tasks: $failed_tasks" + fi + success "Total screenshots: $total_screenshots" + success "Unique screenshots (after OCR): $total_unique" + success "Reduction: $(( (total_screenshots - total_unique) * 100 / total_screenshots ))%" + success "Results backed up to: $BACKUP_DIR" + + log "Task capture completed!" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/run_all_tasks_working.sh b/task-launcher/run_all_tasks_working.sh new file mode 100755 index 000000000..da009b230 --- /dev/null +++ b/task-launcher/run_all_tasks_working.sh @@ -0,0 +1,256 @@ +#!/bin/bash + +# Comprehensive Task Screenshot Capture - Working Version +# Captures screenshots from all 12 tasks with OCR cleanup + +# Configuration +TASKS=("hearts-and-flowers" "egma-math" "matrix-reasoning" "mental-rotation" "memory-game" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") +SCREENSHOT_INTERVAL=8 # seconds between screenshots +TASK_DURATION=180 # 3 minutes per task +WEBPACK_PORT=8080 +BASE_URL="http://localhost:${WEBPACK_PORT}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to check if server is ready +wait_for_server() { + local url=$1 + local max_attempts=30 + local attempt=1 + + log "Waiting for server to be ready at $url..." + + while [ $attempt -le $max_attempts ]; do + if curl -s "$url" > /dev/null 2>&1; then + success "Server is ready!" + return 0 + fi + + echo -n "." + sleep 2 + ((attempt++)) + done + + error "Server failed to start after $max_attempts attempts" + return 1 +} + +# Function to generate Cypress test for a task +generate_cypress_test() { + local task_name=$1 + local test_file="cypress/e2e/${task_name}_working_capture.cy.js" + + cat > "$test_file" << EOF +describe('Task Screenshot Capture - ${task_name}', () => { + it('should capture screenshots with smart interactions', () => { + // Mock fullscreen API to prevent errors + cy.visit('${BASE_URL}/?task=${task_name}', { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + } + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Wait for initial load + cy.wait(3000); + + // Capture screenshots at regular intervals with interactions + for (let i = 0; i < Math.floor(${TASK_DURATION} / ${SCREENSHOT_INTERVAL}); i++) { + // Smart interaction logic + cy.get('body').then((\$body) => { + // Try to click various common interactive elements + const selectors = [ + 'button:contains("OK")', + 'button:contains("Continue")', + 'button:contains("Next")', + 'button:contains("Start")', + 'button:contains("Begin")', + '.jspsych-btn', + 'button[type="button"]', + '.btn', + '[role="button"]', + 'input[type="button"]', + 'input[type="submit"]', + '.corsi-block', + '.afc-stimulus button', + '.response-button', + 'button' + ]; + + // Try each selector until we find clickable elements + let clicked = false; + for (const selector of selectors) { + if (\$body.find(selector).length > 0) { + cy.get(selector).first().then((\$el) => { + if (\$el.is(':visible') && !clicked) { + cy.wrap(\$el).click({ force: true }); + clicked = true; + } + }); + break; + } + } + + // If no specific buttons, try random clicking + if (!clicked) { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Wait for the interval + cy.wait(${SCREENSHOT_INTERVAL} * 1000); + + // Take screenshot + cy.screenshot(\`\${String(i + 1).padStart(2, '0')}-step-\${i}\`); + } + + // Final screenshot + cy.screenshot('99-final'); + }); +}); +EOF + + success "Generated test file: $test_file" +} + +# Function to run OCR cleanup +run_ocr_cleanup() { + local task_name=$1 + local screenshot_dir="cypress/screenshots/${task_name}_working_capture.cy.js" + + if [ -d "$screenshot_dir" ]; then + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + success "OCR cleanup completed for $task_name" + + # Count results + local total_screenshots=$(find "$screenshot_dir" -name "*.png" -not -path "*/duplicates_backup/*" | wc -l) + local backup_screenshots=0 + if [ -d "$screenshot_dir/duplicates_backup" ]; then + backup_screenshots=$(find "$screenshot_dir/duplicates_backup" -name "*.png" | wc -l) + fi + local original_total=$((total_screenshots + backup_screenshots)) + + success "Task $task_name: $original_total total โ†’ $total_screenshots unique ($backup_screenshots duplicates moved to backup)" + else + warning "OCR cleanup failed for $task_name, keeping all screenshots" + fi + else + warning "No screenshots found for $task_name" + fi +} + +# Function to create timestamped backup +create_backup() { + local timestamp=$(date +%Y%m%d_%H%M%S) + local backup_dir="all_tasks_working_$timestamp" + + log "Creating comprehensive backup: $backup_dir" + mkdir -p "$backup_dir" + + # Copy all task screenshot directories + if [ -d "cypress/screenshots" ]; then + cp -r cypress/screenshots/* "$backup_dir/" 2>/dev/null || true + fi + + success "Backup created: $backup_dir" +} + +# Cleanup function +cleanup() { + log "Cleanup initiated..." + if [ ! -z "$WEBPACK_PID" ]; then + kill $WEBPACK_PID 2>/dev/null || true + fi + pkill -f "webpack serve" 2>/dev/null || true + log "Cleanup completed" +} + +# Set trap for cleanup +trap cleanup EXIT INT TERM + +# Main execution +log "Starting comprehensive task screenshot capture (working version)" +log "Tasks to capture: ${TASKS[*]}" +log "Screenshot interval: ${SCREENSHOT_INTERVAL}s, Task duration: ${TASK_DURATION}s" + +# Start webpack dev server +log "Starting webpack dev server on port $WEBPACK_PORT..." +npx webpack serve --mode development --env dbmode=development --port $WEBPACK_PORT > webpack.log 2>&1 & +WEBPACK_PID=$! + +# Wait for server to be ready +if ! wait_for_server "$BASE_URL"; then + error "Failed to start webpack server" + kill $WEBPACK_PID 2>/dev/null || true + exit 1 +fi + +# Process each task +task_count=0 +total_tasks=${#TASKS[@]} + +for task in "${TASKS[@]}"; do + ((task_count++)) + log "Processing task $task_count/$total_tasks: $task" + + # Generate and run Cypress test + log "Starting capture for task: $task" + generate_cypress_test "$task" + + log "Running Cypress test for $task..." + if timeout 300 npx cypress run --spec "cypress/e2e/${task}_working_capture.cy.js" --browser chrome --headless; then + success "Cypress test completed for $task" + + # Run OCR cleanup + run_ocr_cleanup "$task" + else + error "Cypress test failed or timed out for $task" + fi + + # Small delay between tasks + sleep 5 +done + +# Create comprehensive backup +create_backup + +# Stop webpack server +log "Stopping webpack server (PID: $WEBPACK_PID)..." +kill $WEBPACK_PID 2>/dev/null || true +sleep 2 + +success "All tasks completed!" +log "Check the timestamped backup directory for all results" \ No newline at end of file diff --git a/task-launcher/run_complete_tasks.sh b/task-launcher/run_complete_tasks.sh new file mode 100755 index 000000000..2e13ef248 --- /dev/null +++ b/task-launcher/run_complete_tasks.sh @@ -0,0 +1,149 @@ +#!/bin/bash + +# Complete Task Screenshot Capture - Reliable Version +set -e + +TASKS=("memory-game" "egma-math" "matrix-reasoning" "adult-reasoning") +SERVER_PORT=8080 + +echo "๐Ÿš€ Complete Task Screenshot Capture" +echo "๐Ÿ“Š Testing with ${#TASKS[@]} tasks first" + +# Cleanup function +cleanup() { + echo "๐Ÿงน Cleaning up..." + pkill -f "webpack" 2>/dev/null || true + pkill -f "cypress" 2>/dev/null || true + lsof -ti:${SERVER_PORT} | xargs kill -9 2>/dev/null || true + sleep 2 +} + +# Start server +start_server() { + echo "๐Ÿš€ Starting server..." + npx webpack serve --mode development --env dbmode=development --port ${SERVER_PORT} > webpack.log 2>&1 & + + for i in {1..20}; do + if curl -s http://localhost:${SERVER_PORT} > /dev/null 2>&1; then + echo "โœ… Server ready" + return 0 + fi + sleep 2 + done + return 1 +} + +# Test single task first +test_single_task() { + local task=$1 + echo "" + echo "๐ŸŽฏ Testing: $task" + + cat > "cypress/e2e/test_${task}.cy.js" << 'EOF' +describe('TASK_PLACEHOLDER Complete Run', () => { + it('runs complete task with screenshots', () => { + cy.visit('http://localhost:8080/?task=TASK_PLACEHOLDER', { timeout: 60000 }); + + // Mock fullscreen + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + }); + + let counter = 0; + + // Initial screenshot + cy.screenshot(`${String(counter++).padStart(2, '0')}-start`); + cy.wait(2000); + + // Start task + cy.get('body').then($body => { + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + cy.screenshot(`${String(counter++).padStart(2, '0')}-clicked-ok`); + } + }); + + // Main loop - run for reasonable time taking screenshots + for (let i = 0; i < 20; i++) { + cy.wait(8000); + cy.screenshot(`${String(counter++).padStart(2, '0')}-step-${i}`); + + // Check if completed + cy.get('body').then($body => { + const text = $body.text().toLowerCase(); + if (text.includes('thank you') || text.includes('complete') || text.includes('exit')) { + return; // Done + } + + // Continue interacting + if ($body.find('button:contains("OK")').length > 0) { + cy.get('button:contains("OK")').first().click({ force: true }); + } else if ($body.find('button:contains("Continue")').length > 0) { + cy.get('button:contains("Continue")').first().click({ force: true }); + } else if ($body.find('.jspsych-corsi-block').length > 0) { + cy.get('.jspsych-corsi-block').then($blocks => { + cy.wrap($blocks[Math.floor(Math.random() * $blocks.length)]).click({ force: true }); + }); + } else if ($body.find('#jspsych-html-multi-response-btngroup button').length > 0) { + cy.get('#jspsych-html-multi-response-btngroup button').then($buttons => { + cy.wrap($buttons[Math.floor(Math.random() * $buttons.length)]).click({ force: true }); + }); + } else if ($body.find('button').length > 0) { + cy.get('button').first().click({ force: true }); + } + }); + } + + cy.screenshot(`${String(counter++).padStart(2, '0')}-final`); + }); +}); +EOF + + # Replace placeholder with actual task name + sed -i "s/TASK_PLACEHOLDER/$task/g" "cypress/e2e/test_${task}.cy.js" + + # Run test + echo "๐Ÿš€ Running $task test..." + if npx cypress run --spec "cypress/e2e/test_${task}.cy.js" --headless --config defaultCommandTimeout=30000; then + echo "โœ… $task completed" + + # Check results + local dir="cypress/screenshots/test_${task}.cy.js" + if [ -d "$dir" ]; then + local count=$(ls -1 "$dir"/*.png 2>/dev/null | wc -l) + echo "๐Ÿ“ธ Captured $count screenshots" + + # Show some file sizes + echo "๐Ÿ“Š Sample sizes:" + ls -lah "$dir"/*.png | head -5 | awk '{print " " $5 " - " $9}' + + # Run OCR cleanup + echo "๐Ÿ” Running OCR cleanup..." + python3 cleanup_screenshots_ocr.py "$dir/" --execute || echo "โš ๏ธ OCR cleanup failed" + fi + else + echo "โŒ $task failed" + fi + + # Cleanup test file + rm -f "cypress/e2e/test_${task}.cy.js" +} + +# Main execution +cleanup +if ! start_server; then + echo "โŒ Server failed to start" + exit 1 +fi + +# Test each task +for task in "${TASKS[@]}"; do + test_single_task "$task" + sleep 2 +done + +cleanup +echo "๐ŸŽ‰ Complete task testing finished!" \ No newline at end of file diff --git a/task-launcher/run_comprehensive_tests.sh b/task-launcher/run_comprehensive_tests.sh new file mode 100755 index 000000000..8c88b055e --- /dev/null +++ b/task-launcher/run_comprehensive_tests.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +# Comprehensive Task Screenshot Capture - Enhanced Version with Loading Handler +# Runs all 12 tasks with improved error handling and enhanced OCR + +set -e # Exit on any error + +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +BACKUP_DIR="all_task_screenshots_${TIMESTAMP}" +TASKS=("egma-math" "matrix-reasoning" "mental-rotation" "hearts-and-flowers" "memory-game" "same-different-selection" "trog" "vocab" "theory-of-mind" "intro" "roar-inference" "adult-reasoning") + +echo "๐ŸŽฏ COMPREHENSIVE TASK SCREENSHOT CAPTURE - Enhanced v2.0" +echo "==========================================================" +echo "๐Ÿ“ Results will be preserved in: $BACKUP_DIR" +echo "๐Ÿ”ง Using enhanced OCR algorithm with text + image similarity" +echo "๐Ÿš€ Enhanced interaction logic with loading screen handling" +echo "" + +# Create backup directory +mkdir -p "$BACKUP_DIR" + +# Check if server is running +if ! curl -s http://localhost:8080 > /dev/null; then + echo "โŒ Server not running on port 8080. Please start with: ./start_server_clean.sh" + exit 1 +fi + +echo "โœ… Server is running on port 8080" +echo "" + +# Function to run a single task with enhanced interactions +run_task() { + local task=$1 + local task_num=$2 + local total_tasks=$3 + + echo "๐Ÿš€ [$task_num/$total_tasks] Starting task: $task" + echo " ๐Ÿ“ธ Capturing for 8 minutes with enhanced interactions" + echo " ๐Ÿ”„ Loading screen handling enabled" + + # Run Cypress test with longer timeout for loading screens + if timeout 600 npx cypress run --spec "cypress/e2e/${task}_complete.cy.js" --browser chrome --headless; then + echo " โœ… Task $task completed successfully" + + # Run enhanced OCR cleanup with both text and image similarity + local screenshot_dir="cypress/screenshots/${task}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + echo " ๐Ÿงน Running enhanced OCR cleanup (text + image similarity)..." + echo " - Text similarity threshold: โ‰ฅ80%" + echo " - Image similarity threshold: โ‰ฅ95%" + echo " - Preserving visually unique content" + + python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute + + # Show results summary + local unique_count=$(find "$screenshot_dir" -name "*.png" ! -path "*/duplicates_backup/*" | wc -l) + local duplicate_count=$(find "$screenshot_dir/duplicates_backup" -name "*.png" 2>/dev/null | wc -l || echo "0") + local total_original=$((unique_count + duplicate_count)) + local reduction_percent=0 + if [ $total_original -gt 0 ]; then + reduction_percent=$(( (duplicate_count * 100) / total_original )) + fi + + echo " โœจ Results: $unique_count unique, $duplicate_count duplicates (${reduction_percent}% reduction)" + + # Copy results to backup + cp -r "$screenshot_dir" "$BACKUP_DIR/" + echo " ๐Ÿ’พ Results backed up to $BACKUP_DIR" + else + echo " โš ๏ธ No screenshots directory found for $task" + fi + else + echo " โŒ Task $task failed or timed out" + # Still try to backup any partial results + local screenshot_dir="cypress/screenshots/${task}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + cp -r "$screenshot_dir" "$BACKUP_DIR/" + echo " ๐Ÿ’พ Partial results backed up" + fi + fi + + echo "" +} + +# Run all tasks +echo "๐ŸŽฌ Starting comprehensive capture of ${#TASKS[@]} tasks..." +echo "" + +task_num=1 +for task in "${TASKS[@]}"; do + run_task "$task" $task_num ${#TASKS[@]} + ((task_num++)) + + # Brief pause between tasks to let server stabilize + sleep 3 +done + +echo "๐ŸŽ‰ COMPREHENSIVE CAPTURE COMPLETE!" +echo "๐Ÿ“Š Summary:" +echo " Total tasks attempted: ${#TASKS[@]}" +echo " Backup location: $BACKUP_DIR" +echo " Enhanced OCR: Text + Image similarity analysis" +echo "" +echo "๐Ÿ“‹ Results summary:" +total_unique=0 +total_duplicates=0 +successful_tasks=0 + +for task in "${TASKS[@]}"; do + screenshot_dir="cypress/screenshots/${task}_complete.cy.js" + if [ -d "$screenshot_dir" ]; then + unique_count=$(find "$screenshot_dir" -name "*.png" ! -path "*/duplicates_backup/*" | wc -l) + duplicate_count=$(find "$screenshot_dir/duplicates_backup" -name "*.png" 2>/dev/null | wc -l || echo "0") + total_original=$((unique_count + duplicate_count)) + + if [ $total_original -gt 0 ]; then + reduction_percent=$(( (duplicate_count * 100) / total_original )) + echo " โœ… $task: $unique_count unique, $duplicate_count duplicates (${reduction_percent}% reduction)" + total_unique=$((total_unique + unique_count)) + total_duplicates=$((total_duplicates + duplicate_count)) + successful_tasks=$((successful_tasks + 1)) + else + echo " โŒ $task: No screenshots captured" + fi + else + echo " โŒ $task: No results found" + fi +done + +echo "" +echo "๐Ÿ“ˆ Overall Statistics:" +echo " Successful tasks: $successful_tasks/${#TASKS[@]}" +echo " Total unique screenshots: $total_unique" +echo " Total duplicates removed: $total_duplicates" +if [ $((total_unique + total_duplicates)) -gt 0 ]; then + overall_reduction=$(( (total_duplicates * 100) / (total_unique + total_duplicates) )) + echo " Overall reduction: ${overall_reduction}%" +fi +echo "" +echo "โœจ All results preserved in: $BACKUP_DIR" +echo "๐Ÿ” Enhanced OCR algorithm successfully applied to all tasks" \ No newline at end of file diff --git a/task-launcher/run_single_task.sh b/task-launcher/run_single_task.sh new file mode 100755 index 000000000..c6f675bc3 --- /dev/null +++ b/task-launcher/run_single_task.sh @@ -0,0 +1,342 @@ +#!/bin/bash + +# Single Task Runner Script +# Usage: ./run_single_task.sh [task-name] +# Example: ./run_single_task.sh theory-of-mind + +if [ $# -eq 0 ]; then + echo "๐ŸŽฏ Single Task Screenshot Capture" + echo "Usage: $0 " + echo "" + echo "Available tasks:" + echo " theory-of-mind" + echo " egma-math" + echo " memory-game" + echo " matrix-reasoning" + echo " hearts-and-flowers" + echo " mental-rotation" + echo " same-different-selection" + echo " trog" + echo " vocab" + echo " roar-inference" + echo " adult-reasoning" + echo " intro" + echo "" + echo "Example: $0 theory-of-mind" + exit 1 +fi + +TASK_NAME=$1 +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +LOG_FILE="cypress_${TASK_NAME}_${TIMESTAMP}.log" + +echo "๐Ÿš€ Starting screenshot capture for: $TASK_NAME" +echo "๐Ÿ“ Log file: $LOG_FILE" +echo "๐Ÿ“ธ Screenshots will be timestamped and saved" +echo "" + +# Create a temporary test file with the specific task +cat > cypress/e2e/temp_single_task.cy.js << EOF +// Temporary single task capture for: $TASK_NAME +const TASK_NAME = '$TASK_NAME'; + +describe(\`Single Task Capture - \${TASK_NAME}\`, () => { + it(\`captures complete \${TASK_NAME} task until completion\`, () => { + let screenshotCounter = 0; + let maxSteps = 200; // Increased limit to capture complete tasks like Theory of Mind + let completionDetected = false; + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + + // Handle DOM detachment and other errors + Cypress.on('uncaught:exception', (err, runnable) => { + if (err.message.includes('detached from the DOM') || + err.message.includes('not attached') || + err.message.includes('requery') || + err.message.includes('Permissions check failed') || + err.message.includes('fullscreen')) { + console.log('Ignoring DOM/permission/fullscreen error:', err.message); + return false; + } + return true; + }); + + // Enhanced fullscreen mocking function + function visitWithFullscreenMocking(url) { + cy.visit(url, { + timeout: 120000, // 2 minutes timeout for initial load + onBeforeLoad: (win) => { + // Enhanced fullscreen mocking + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + win.document.exitFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + Object.defineProperty(win.screen, 'orientation', { + value: { lock: cy.stub().resolves() }, + writable: true + }); + + // Additional mocking + win.screen.lockOrientation = cy.stub().resolves(); + win.screen.mozLockOrientation = cy.stub().resolves(); + win.screen.msLockOrientation = cy.stub().resolves(); + + // Even more aggressive fullscreen mocking + win.document.webkitRequestFullscreen = cy.stub().resolves(); + win.document.mozRequestFullScreen = cy.stub().resolves(); + win.document.msRequestFullscreen = cy.stub().resolves(); + win.document.webkitExitFullscreen = cy.stub().resolves(); + win.document.mozCancelFullScreen = cy.stub().resolves(); + win.document.msExitFullscreen = cy.stub().resolves(); + + // Mock fullscreen change events + win.document.addEventListener = cy.stub(); + win.document.removeEventListener = cy.stub(); + + // Override any fullscreen checks + Object.defineProperty(win.document, 'webkitFullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'mozFullScreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'msFullscreenElement', { + get: () => win.document.documentElement + }); + + // Mock screen properties + Object.defineProperty(win.screen, 'width', { value: 1920 }); + Object.defineProperty(win.screen, 'height', { value: 1080 }); + Object.defineProperty(win.screen, 'availWidth', { value: 1920 }); + Object.defineProperty(win.screen, 'availHeight', { value: 1080 }); + } + }); + } + + // Visit with enhanced fullscreen mocking + visitWithFullscreenMocking(\`http://localhost:8080/?task=\${TASK_NAME}\`); + + // Take initial screenshot with timestamp + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-initial-load\`); + cy.wait(3000); + + // Flexible task start - try multiple approaches + cy.get('body').then((\$body) => { + // Try to find and click OK/Start button, but don't require it + if (\$body.find('button:contains("OK"), button:contains("Start"), button:contains("Begin"), button:contains("Continue"), button:contains("Get Started")').length > 0) { + console.log('๐ŸŽฏ Found start button, clicking...'); + cy.contains('OK', 'Start', 'Begin', 'Continue', 'Get Started').first().click(); + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-task-started\`); + } else { + console.log('โšก No start button found, proceeding directly to task loop'); + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-no-start-button\`); + } + }); + + cy.wait(2000); + + // Main task loop with completion detection + function taskLoopWithCompletion(stepNumber) { + if (stepNumber > maxSteps || completionDetected) { + console.log(\`๐Ÿ Stopping: Step limit (\${maxSteps}) reached or completion detected\`); + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-final-state\`); + return; + } + + console.log(\`๐Ÿ“ธ Step \${stepNumber}/\${maxSteps} - Screenshot \${screenshotCounter}\`); + + // Wait for stimulus container or check for completion + cy.get('body').then((\$body) => { + // Check for task completion indicators + if (\$body.find('footer').length > 0) { + console.log('๐Ÿ Task completed - found footer'); + completionDetected = true; + cy.contains('Exit').click({ timeout: 60000 }); + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-task-completed\`); + return; + } + + // Check if stimulus container exists + if (\$body.find('.lev-stimulus-container').length > 0) { + // Take screenshot of current state + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-step-\${stepNumber}\`); + cy.wait(2000); + + // Handle interactions based on task type and available elements + handleTaskInteractions(\$body, TASK_NAME); + + cy.wait(3000); + + // Continue to next step + taskLoopWithCompletion(stepNumber + 1); + } else { + console.log('โน๏ธ No stimulus container found - task may be finished'); + cy.screenshot(\`\${timestamp}-\${TASK_NAME}-\${(++screenshotCounter).toString().padStart(3, '0')}-no-stimulus\`); + + // Try to continue anyway + cy.wait(5000); + taskLoopWithCompletion(stepNumber + 1); + } + }); + } + + function handleTaskInteractions(\$body, taskName) { + cy.get('.jspsych-content').then((content) => { + // Priority 1: Correct answer buttons (key insight from working tests) + const correctButtons = content.find('.correct'); + const correctAriaButtons = content.find('[aria-label="correct"]'); + + if (correctButtons.length > 0) { + console.log('๐ŸŽฏ Clicking correct answer button'); + cy.get('.correct').first().click({ timeout: 30000 }); + return; + } + + if (correctAriaButtons.length > 0) { + console.log('๐ŸŽฏ Clicking correct aria-label button'); + cy.get('[aria-label="correct"]').first().click({ timeout: 30000 }); + return; + } + + // Task-specific interactions + if (taskName.includes('math') || taskName.includes('egma')) { + handleMathInteractions(content); + } else if (taskName.includes('theory') || taskName.includes('mind')) { + handleTheoryOfMindInteractions(content); + } else if (taskName.includes('memory')) { + handleMemoryInteractions(content); + } else if (taskName.includes('matrix') || taskName.includes('reasoning')) { + handleMatrixInteractions(content); + } else if (taskName.includes('hearts') || taskName.includes('flowers')) { + handleHeartsFlowersInteractions(content); + } else { + handleGenericInteractions(content); + } + }); + } + + function handleMathInteractions(content) { + const sliders = content.find('.jspsych-slider'); + const numberInputs = content.find('input[type="number"]'); + const textInputs = content.find('input[type="text"]'); + const primaryButtons = content.find('.primary'); + + if (sliders.length > 0 && content.find('.secondary').length === 0) { + console.log('๐ŸŽš๏ธ Handling math slider'); + cy.get('.jspsych-slider').click(); + cy.wait(1000); + if (primaryButtons.length > 0) { + cy.get('.primary').click(); + } + } else if (numberInputs.length > 0) { + console.log('๐Ÿ”ข Handling number input'); + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.get('input[type="number"]').first().clear().type(randomNumber.toString()); + cy.wait(500); + if (primaryButtons.length > 0) { + cy.get('.primary').click(); + } + } else if (textInputs.length > 0) { + console.log('๐Ÿ“ Handling text input'); + cy.get('input[type="text"]').first().clear().type('5'); + cy.wait(500); + if (primaryButtons.length > 0) { + cy.get('.primary').click(); + } + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } + } + + function handleTheoryOfMindInteractions(content) { + const imageButtons = content.find('.image'); + const primaryButtons = content.find('.primary'); + + if (imageButtons.length > 0) { + console.log('๐Ÿ–ผ๏ธ Clicking image button'); + cy.get('.image').first().click(); + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } + } + + function handleMemoryInteractions(content) { + const cards = content.find('.card, .memory-card, [data-card]'); + const primaryButtons = content.find('.primary'); + + if (cards.length > 0) { + console.log('๐Ÿƒ Clicking memory card'); + cy.get('.card, .memory-card, [data-card]').first().click(); + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } + } + + function handleMatrixInteractions(content) { + const matrixButtons = content.find('.matrix-option, .option'); + const primaryButtons = content.find('.primary'); + + if (matrixButtons.length > 0) { + console.log('๐Ÿ”ฒ Clicking matrix option'); + cy.get('.matrix-option, .option').first().click(); + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } + } + + function handleHeartsFlowersInteractions(content) { + const directionButtons = content.find('[data-direction], .direction'); + const primaryButtons = content.find('.primary'); + + if (directionButtons.length > 0) { + console.log('โžก๏ธ Clicking direction button'); + cy.get('[data-direction], .direction').first().click(); + } else if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } + } + + function handleGenericInteractions(content) { + const primaryButtons = content.find('.primary'); + const anyButton = content.find('button:visible:not([disabled])'); + + if (primaryButtons.length > 0) { + console.log('โ–ถ๏ธ Clicking primary button'); + cy.get('.primary').first().click(); + } else if (anyButton.length > 0) { + console.log('๐Ÿ”˜ Clicking any available button'); + cy.get('button:visible:not([disabled])').first().click({ force: true }); + } + } + + // Start the main task loop + taskLoopWithCompletion(1); + }); +}); +EOF + +echo "โณ Running Cypress test..." +echo "๐Ÿ’ก Press Ctrl+C to stop the test at any time" +echo "" + +# Run the test and capture output +npx cypress run --spec "cypress/e2e/temp_single_task.cy.js" --headless 2>&1 | tee "$LOG_FILE" + +# Clean up temporary file +rm -f cypress/e2e/temp_single_task.cy.js + +echo "" +echo "โœ… Test completed!" +echo "๐Ÿ“ Full log saved to: $LOG_FILE" +echo "๐Ÿ“ธ Screenshots saved in: cypress/screenshots/" +echo "" +echo "๐Ÿ” To analyze screenshots with OCR:" +echo "cd .. && python3 cleanup_screenshots_ocr.py task-launcher/cypress/screenshots/temp_single_task.cy.js/" \ No newline at end of file diff --git a/task-launcher/run_tasks_preserving.sh b/task-launcher/run_tasks_preserving.sh new file mode 100755 index 000000000..2d02c137d --- /dev/null +++ b/task-launcher/run_tasks_preserving.sh @@ -0,0 +1,266 @@ +#!/bin/bash + +# Task Screenshot Capture with Preservation +# This version preserves ALL screenshots from each task + +set -e + +# Configuration +SCREENSHOT_INTERVAL=6 # seconds between screenshots +TASK_DURATION=240 # 4 minutes per task +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +RESULTS_DIR="all_tasks_results_${TIMESTAMP}" + +# All 12 tasks +TASKS="hearts-and-flowers egma-math matrix-reasoning mental-rotation memory-game same-different-selection trog vocab theory-of-mind intro roar-inference adult-reasoning" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Create results directory +mkdir -p "$RESULTS_DIR" + +log "Starting task screenshot capture with preservation" +log "Results will be saved to: $RESULTS_DIR" +log "Tasks to capture: $TASKS" + +# Function to ensure webpack server is running +ensure_webpack_server() { + log "Checking webpack server status..." + if ! curl -s http://localhost:8080 > /dev/null; then + log "Starting webpack dev server on port 8080..." + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + log "Waiting for server to be ready..." + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null; then + success "Webpack server is ready!" + return 0 + fi + sleep 2 + echo -n "." + done + error "Webpack server failed to start" + return 1 + else + success "Webpack server is already running" + fi +} + +# Function to generate a working Cypress test +generate_test() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_preserving.cy.js" + + cat > "$test_file" << 'EOF' +describe('Task Screenshot Capture', () => { + it('should capture screenshots with smart interactions', () => { + const taskName = Cypress.spec.name.split('_')[0]; + let screenshotCounter = 0; + + // Mock fullscreen API + cy.visit(`http://localhost:8080/?task=${taskName}`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + } + }); + + // Initial screenshot + cy.screenshot(`00-start`); + + // Function to take screenshot and interact + const captureAndInteract = () => { + cy.screenshot(`${String(screenshotCounter + 1).padStart(2, '0')}-step-${screenshotCounter}`); + screenshotCounter++; + + // Smart interaction logic with multiple fallbacks + cy.get('body').then(($body) => { + // Priority 1: Continue/Next/OK buttons + if ($body.find('button:contains("Continue"), button:contains("Next"), button:contains("OK"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("Next"), button:contains("OK"), button:contains("Start")').first().click(); + } + // Priority 2: Task-specific interactions + else if ($body.find('.corsi-block, [data-choice], .afc-stimulus button, input[type="range"]').length > 0) { + // Memory game Corsi blocks + if ($body.find('.corsi-block').length > 0) { + cy.get('.corsi-block').first().click(); + } + // Multiple choice buttons + else if ($body.find('[data-choice]').length > 0) { + cy.get('[data-choice]').then($choices => { + const randomIndex = Math.floor(Math.random() * $choices.length); + cy.wrap($choices[randomIndex]).click(); + }); + } + // AFC stimulus buttons + else if ($body.find('.afc-stimulus button').length > 0) { + cy.get('.afc-stimulus button').then($buttons => { + const randomIndex = Math.floor(Math.random() * $buttons.length); + cy.wrap($buttons[randomIndex]).click(); + }); + } + // Sliders for math tasks + else if ($body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().invoke('val', Math.floor(Math.random() * 100)).trigger('input'); + } + } + // Priority 3: Any clickable button + else if ($body.find('button:not(:disabled)').length > 0) { + cy.get('button:not(:disabled)').first().click(); + } + // Priority 4: Keyboard interactions + else { + cy.get('body').type(' '); // Space key + } + }); + }; + + // Capture screenshots at intervals + const totalScreenshots = Math.floor(TASK_DURATION / SCREENSHOT_INTERVAL); + for (let i = 0; i < totalScreenshots; i++) { + cy.wait(SCREENSHOT_INTERVAL * 1000); + captureAndInteract(); + } + + // Final screenshot + cy.wait(2000); + cy.screenshot('99-final'); + }); +}); +EOF + + # Replace TASK_DURATION and SCREENSHOT_INTERVAL with actual values + sed -i "s/TASK_DURATION/${TASK_DURATION}/g" "$test_file" + sed -i "s/SCREENSHOT_INTERVAL/${SCREENSHOT_INTERVAL}/g" "$test_file" + + success "Generated test file: $test_file" +} + +# Function to run a single task +run_task() { + local task_name="$1" + local task_number="$2" + local total_tasks="$3" + + log "Processing task $task_number/$total_tasks: $task_name" + + # Generate test file + generate_test "$task_name" + + # Run Cypress test + log "Running Cypress test for $task_name..." + if timeout ${TASK_DURATION}s npx cypress run --spec "cypress/e2e/${task_name}_preserving.cy.js" --browser chrome; then + success "Cypress test completed for $task_name" + + # Move screenshots to results directory + local source_dir="cypress/screenshots/${task_name}_preserving.cy.js" + local dest_dir="$RESULTS_DIR/${task_name}" + + if [ -d "$source_dir" ]; then + mkdir -p "$dest_dir" + cp -r "$source_dir"/* "$dest_dir/" + local screenshot_count=$(ls "$dest_dir"/*.png 2>/dev/null | wc -l) + success "Preserved $screenshot_count screenshots for $task_name in $dest_dir" + + # Run OCR cleanup + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$dest_dir" --execute; then + success "OCR cleanup completed for $task_name" + else + warn "OCR cleanup failed for $task_name, but screenshots are preserved" + fi + else + warn "No screenshots found for $task_name" + fi + + # Clean up cypress screenshots to free space + rm -rf "$source_dir" + + else + error "Cypress test failed or timed out for $task_name" + fi + + # Clean up test file + rm -f "cypress/e2e/${task_name}_preserving.cy.js" +} + +# Main execution +main() { + log "Starting comprehensive task capture with preservation" + + # Ensure webpack server is running + ensure_webpack_server + + # Process each task + task_counter=1 + total_tasks=$(echo $TASKS | wc -w) + + for task in $TASKS; do + log "=== TASK $task_counter/$total_tasks: $task ===" + run_task "$task" "$task_counter" "$total_tasks" + task_counter=$((task_counter + 1)) + + # Brief pause between tasks + sleep 5 + done + + # Final summary + log "=== FINAL SUMMARY ===" + log "All tasks completed. Results saved to: $RESULTS_DIR" + + for task in $TASKS; do + local task_dir="$RESULTS_DIR/${task}" + if [ -d "$task_dir" ]; then + local count=$(ls "$task_dir"/*.png 2>/dev/null | wc -l || echo "0") + log " $task: $count screenshots" + else + log " $task: No screenshots" + fi + done + + success "All tasks completed! Check $RESULTS_DIR for all screenshots" +} + +# Cleanup function +cleanup() { + log "Cleanup initiated..." + if [ ! -z "$WEBPACK_PID" ]; then + log "Stopping webpack server (PID: $WEBPACK_PID)..." + kill $WEBPACK_PID 2>/dev/null || true + fi + log "Cleanup completed" +} + +# Set trap for cleanup +trap cleanup EXIT + +# Run main function +main "$@" \ No newline at end of file diff --git a/task-launcher/run_tasks_simple.sh b/task-launcher/run_tasks_simple.sh new file mode 100755 index 000000000..d63855736 --- /dev/null +++ b/task-launcher/run_tasks_simple.sh @@ -0,0 +1,257 @@ +#!/bin/bash + +# Simple Task Screenshot Capture - One task at a time with monitoring +# This version is more reliable and easier to debug + +# Configuration +SCREENSHOT_INTERVAL=6 # seconds between screenshots +TASK_DURATION=240 # 4 minutes per task +TASK_NAME="${1:-hearts-and-flowers}" # Default to hearts-and-flowers, or use first argument + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { + echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Function to ensure webpack server is running +ensure_webpack_server() { + log "Checking webpack server status..." + + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is already running" + return 0 + fi + + log "Starting webpack dev server..." + # Kill any existing webpack processes + pkill -f "webpack.*8080" || true + sleep 2 + + # Start webpack server in background + npx webpack serve --mode development --env dbmode=development --port 8080 > webpack.log 2>&1 & + WEBPACK_PID=$! + + # Wait for server to be ready + log "Waiting for webpack server to start..." + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null 2>&1; then + success "Webpack server is ready!" + return 0 + fi + sleep 1 + echo -n "." + done + + error "Webpack server failed to start after 30 seconds" + return 1 +} + +# Function to generate Cypress test +generate_task_test() { + local task_name="$1" + local test_file="cypress/e2e/${task_name}_simple.cy.js" + + log "Generating test for $task_name..." + + cat > "$test_file" << EOF +describe('$task_name Simple Task Capture', () => { + it('should capture screenshots while progressing through task', () => { + const taskName = '$task_name'; + const screenshotInterval = $SCREENSHOT_INTERVAL * 1000; + const maxDuration = $TASK_DURATION * 1000; + let screenshotCount = 0; + + // Visit task with fullscreen API mocking + cy.visit(\`http://localhost:8080/?task=\${taskName}\`, { + onBeforeLoad(win) { + // Mock fullscreen API + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement, + configurable: true + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true, + configurable: true + }); + + // Mock audio context + win.AudioContext = win.AudioContext || win.webkitAudioContext || function() { + return { + createOscillator: () => ({ + connect: () => {}, + start: () => {}, + stop: () => {}, + frequency: { value: 440 } + }), + createGain: () => ({ + connect: () => {}, + gain: { value: 0.5 } + }), + destination: {}, + currentTime: 0, + state: 'running' + }; + }; + } + }); + + // Initial screenshot + cy.screenshot(\`00-start\`, { capture: 'viewport' }); + screenshotCount++; + + // Main interaction loop + const startTime = Date.now(); + + function captureAndInteract() { + const currentTime = Date.now(); + if (currentTime - startTime > maxDuration) { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + return; + } + + // Take screenshot + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-step-\${screenshotCount - 1}\`, { capture: 'viewport' }); + screenshotCount++; + + // Smart interaction logic + cy.get('body').then(\$body => { + // Priority 1: Continue/OK/Next buttons + if (\$body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').length > 0) { + cy.get('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start")').first().click({ force: true }); + } + // Priority 2: Multiple choice buttons + else if (\$body.find('#jspsych-html-multi-response-btngroup button').length >= 2) { + const buttons = \$body.find('#jspsych-html-multi-response-btngroup button'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('#jspsych-html-multi-response-btngroup button').eq(randomIndex).click({ force: true }); + } + // Priority 3: Any enabled buttons + else if (\$body.find('button:not([disabled])').length > 0) { + const buttons = \$body.find('button:not([disabled])'); + const randomIndex = Math.floor(Math.random() * buttons.length); + cy.get('button:not([disabled])').eq(randomIndex).click({ force: true }); + } + // Priority 4: Sliders (for math tasks) + else if (\$body.find('input[type="range"]').length > 0) { + cy.get('input[type="range"]').first().then(\$slider => { + const min = \$slider.attr('min') || 0; + const max = \$slider.attr('max') || 100; + const randomValue = Math.floor(Math.random() * (max - min + 1)) + parseInt(min); + cy.wrap(\$slider).invoke('val', randomValue).trigger('input').trigger('change'); + }); + } + // Priority 5: Clickable elements + else if (\$body.find('.clickable, [onclick]').length > 0) { + cy.get('.clickable, [onclick]').first().click({ force: true }); + } + // Fallback: Click center of screen + else { + cy.get('body').click(500, 300, { force: true }); + } + }); + + // Continue loop + cy.wait(screenshotInterval).then(() => { + if (Date.now() - startTime < maxDuration) { + captureAndInteract(); + } else { + cy.screenshot(\`\${String(screenshotCount).padStart(2, '0')}-final\`, { capture: 'viewport' }); + } + }); + } + + // Start the capture loop + cy.wait(3000).then(() => { + captureAndInteract(); + }); + }); +}); +EOF + + success "Generated test file: $test_file" +} + +# Function to run single task +run_single_task() { + local task_name="$1" + + log "Starting capture for task: $task_name" + log "Duration: ${TASK_DURATION}s, Interval: ${SCREENSHOT_INTERVAL}s" + + # Ensure webpack server is running + if ! ensure_webpack_server; then + error "Failed to start webpack server" + return 1 + fi + + # Generate test + generate_task_test "$task_name" + + # Run Cypress test + log "Running Cypress test for $task_name..." + if timeout $((TASK_DURATION + 60)) npx cypress run --spec "cypress/e2e/${task_name}_simple.cy.js" --browser electron --config video=false; then + success "Cypress test completed for $task_name" + + # Check results + screenshot_dir="cypress/screenshots/${task_name}_simple.cy.js" + if [[ -d "$screenshot_dir" ]]; then + screenshot_count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + success "Task $task_name: $screenshot_count screenshots captured" + + # Show file sizes to verify progression + log "Screenshot file sizes:" + ls -lh "$screenshot_dir"/*.png | head -10 + + # Run OCR cleanup + log "Running OCR cleanup for $task_name..." + if python3 cleanup_screenshots_ocr.py "$screenshot_dir" --execute; then + remaining_count=$(ls "$screenshot_dir"/*.png 2>/dev/null | wc -l) + success "OCR cleanup completed: $screenshot_count โ†’ $remaining_count screenshots" + else + warn "OCR cleanup failed, keeping all screenshots" + fi + else + error "No screenshots found for $task_name" + return 1 + fi + else + error "Cypress test failed or timed out for $task_name" + return 1 + fi + + # Clean up test file + rm -f "cypress/e2e/${task_name}_simple.cy.js" + + success "Completed task: $task_name" + return 0 +} + +# Main execution +log "Starting simple task screenshot capture" +log "Task: $TASK_NAME" + +if run_single_task "$TASK_NAME"; then + success "Task completed successfully!" +else + error "Task failed!" + exit 1 +fi \ No newline at end of file diff --git a/task-launcher/run_tasks_with_backup.sh b/task-launcher/run_tasks_with_backup.sh new file mode 100755 index 000000000..86a3f81ef --- /dev/null +++ b/task-launcher/run_tasks_with_backup.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Run tasks with immediate backup to prevent Cypress from overwriting +TASKS=("memory-game" "hearts-and-flowers" "matrix-reasoning" "egma-math" "mental-rotation") +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="all_tasks_backup_$TIMESTAMP" + +echo "Creating backup directory: $BACKUP_DIR" +mkdir -p "$BACKUP_DIR" + +for task in "${TASKS[@]}"; do + echo "=== Processing task: $task ===" + + # Run Cypress test + echo "Running Cypress test for $task..." + npx cypress run --spec "cypress/e2e/${task}_quick.cy.js" --browser chrome --headless + + # Backup screenshots immediately + if [ -d "cypress/screenshots/${task}_quick.cy.js" ]; then + echo "Backing up screenshots for $task..." + cp -r "cypress/screenshots/${task}_quick.cy.js" "$BACKUP_DIR/" + + # Count screenshots + screenshot_count=$(find "cypress/screenshots/${task}_quick.cy.js" -name "*.png" | wc -l) + echo "Captured $screenshot_count screenshots for $task" + else + echo "No screenshots found for $task" + fi + + echo "Completed $task" + echo "" + sleep 2 +done + +echo "All tasks completed! Screenshots backed up in: $BACKUP_DIR" +echo "Contents:" +ls -la "$BACKUP_DIR" \ No newline at end of file diff --git a/task-launcher/setup_ocr.sh b/task-launcher/setup_ocr.sh new file mode 100755 index 000000000..c767f982f --- /dev/null +++ b/task-launcher/setup_ocr.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Setup script for OCR-based screenshot cleanup + +echo "๐Ÿ”ง Setting up OCR Screenshot Cleanup..." + +# Install system dependencies +echo "๐Ÿ“ฆ Installing system packages..." +if command -v apt-get &> /dev/null; then + # Ubuntu/Debian + sudo apt-get update + sudo apt-get install -y tesseract-ocr tesseract-ocr-eng python3-pip +elif command -v brew &> /dev/null; then + # macOS + brew install tesseract +elif command -v yum &> /dev/null; then + # RHEL/CentOS + sudo yum install -y tesseract python3-pip +else + echo "โš ๏ธ Please install tesseract-ocr manually for your system" +fi + +# Install Python dependencies +echo "๐Ÿ Installing Python packages..." +pip3 install -r requirements_ocr.txt + +# Make script executable +chmod +x cleanup_screenshots_ocr.py + +# Test installation +echo "๐Ÿงช Testing installation..." +python3 -c "import pytesseract; from PIL import Image; print('โœ… OCR setup complete!')" + +echo "" +echo "๐ŸŽ‰ Setup complete! Usage examples:" +echo " # Dry run (preview only):" +echo " python3 cleanup_screenshots_ocr.py cypress/screenshots/memory_simple_complete.cy.js/" +echo "" +echo " # Execute cleanup with 80% similarity threshold:" +echo " python3 cleanup_screenshots_ocr.py cypress/screenshots/memory_simple_complete.cy.js/ --execute" +echo "" +echo " # More aggressive cleanup (90% similarity):" +echo " python3 cleanup_screenshots_ocr.py cypress/screenshots/memory_simple_complete.cy.js/ --similarity 0.9 --execute" \ No newline at end of file diff --git a/task-launcher/start_server_clean.sh b/task-launcher/start_server_clean.sh new file mode 100755 index 000000000..0fb197b84 --- /dev/null +++ b/task-launcher/start_server_clean.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +echo "๐Ÿงน Cleaning up existing servers..." + +# Kill any existing webpack dev servers +pkill -f "webpack serve" 2>/dev/null || true +pkill -f "npm run dev" 2>/dev/null || true + +# Kill processes on ports 8080 and 8081 +lsof -ti:8080 | xargs kill -9 2>/dev/null || true +lsof -ti:8081 | xargs kill -9 2>/dev/null || true + +# Wait a moment for cleanup +sleep 2 + +echo "โœ… Cleanup complete" + +# Navigate to the correct directory +cd "$(dirname "$0")" + +echo "๐Ÿ“ Starting server from: $(pwd)" + +# Check if we have the correct package.json +if [ ! -f "package.json" ]; then + echo "โŒ Error: package.json not found in current directory" + echo "Make sure you're in the task-launcher directory" + exit 1 +fi + +# Check if npm run dev is available +if ! npm run 2>&1 | grep -q "dev"; then + echo "โŒ Error: 'npm run dev' script not found" + echo "Available scripts:" + npm run + exit 1 +fi + +echo "๐Ÿš€ Starting webpack dev server..." +echo "Server will be available at: http://localhost:8080/" + +# Start the server +npm run dev \ No newline at end of file diff --git a/task-launcher/update_all_tests_enhanced.sh b/task-launcher/update_all_tests_enhanced.sh new file mode 100755 index 000000000..1c5b212ed --- /dev/null +++ b/task-launcher/update_all_tests_enhanced.sh @@ -0,0 +1,347 @@ +#!/bin/bash + +echo "๐Ÿ”ง UPDATING ALL TASK TESTS WITH ENHANCED ANTI-LOOP LOGIC" +echo "========================================================" + +# List of all tasks +TASKS=( + "egma-math" + "matrix-reasoning" + "mental-rotation" + "hearts-and-flowers" + "memory-game" + "same-different-selection" + "trog" + "vocab" + "theory-of-mind" + "intro" + "roar-inference" + "adult-reasoning" +) + +# Enhanced interaction template +create_enhanced_test() { + local task_name=$1 + local task_kebab=$2 + + cat > "cypress/e2e/${task_kebab}_enhanced.cy.js" << 'EOL' +describe('TASK_NAME Enhanced - Anti-Loop Logic', () => { + it('should capture screenshots with intelligent progression', () => { + let lastScreenState = ''; + let stateRepeatCount = 0; + let interactionStrategy = 0; + let visitedStates = new Set(); + + // Visit the task + cy.visit('http://localhost:8080?task=TASK_KEBAB'); + + // Mock fullscreen API + cy.window().then((win) => { + win.document.documentElement.requestFullscreen = cy.stub().resolves(); + Object.defineProperty(win.document, 'fullscreenElement', { + get: () => win.document.documentElement + }); + Object.defineProperty(win.document, 'fullscreenEnabled', { + get: () => true + }); + win.screen.orientation = { + lock: cy.stub().resolves() + }; + }); + + // Take initial screenshot + cy.screenshot('00-start'); + + // Enhanced interaction with loop detection (30 steps = 2.5 minutes) + for (let i = 1; i <= 30; i++) { + cy.wait(5000); // 5 second intervals + + // Capture current screen state for loop detection + cy.get('body').then(($body) => { + // Create a more sophisticated state identifier + const visibleText = $body.find(':visible').text().replace(/\s+/g, ' ').trim(); + const buttonCount = $body.find('button:visible').length; + const inputCount = $body.find('input:visible').length; + const currentScreenState = `${visibleText.substring(0, 150)}_BTN:${buttonCount}_INP:${inputCount}`; + + // Detect if we're stuck in the same state + if (currentScreenState === lastScreenState) { + stateRepeatCount++; + console.log(`๐Ÿ”„ Same state detected ${stateRepeatCount} times: ${currentScreenState.substring(0, 50)}...`); + } else { + // New state detected + if (visitedStates.has(currentScreenState)) { + console.log(`๐Ÿ” Revisiting known state: ${currentScreenState.substring(0, 50)}...`); + stateRepeatCount = 1; // Start counting repeats + } else { + console.log(`โœจ New state discovered: ${currentScreenState.substring(0, 50)}...`); + visitedStates.add(currentScreenState); + stateRepeatCount = 0; + interactionStrategy = 0; // Reset strategy for new states + } + } + lastScreenState = currentScreenState; + + // Escalate strategy if stuck + if (stateRepeatCount >= 2) { + interactionStrategy = Math.min(interactionStrategy + 1, 5); + console.log(`๐Ÿšจ Escalating to strategy ${interactionStrategy} (stuck ${stateRepeatCount} times)`); + } + + // Progressive interaction strategies + performEnhancedInteraction($body, interactionStrategy, i); + }); + + // Take screenshot + cy.screenshot(`${i.toString().padStart(2, '0')}-step-${i}`); + } + + // Final screenshot + cy.screenshot('99-final'); + + function performEnhancedInteraction($body, strategy, step) { + console.log(`๐ŸŽฏ Step ${step}: Using interaction strategy ${strategy}`); + + switch(strategy) { + case 0: // Normal progression + normalInteraction($body); + break; + case 1: // More thorough exploration + thoroughInteraction($body); + break; + case 2: // Alternative elements + alternativeInteraction($body); + break; + case 3: // Keyboard navigation + keyboardInteraction($body); + break; + case 4: // Random exploration + randomInteraction($body); + break; + case 5: // Force progression + forceProgressionInteraction($body); + break; + default: + normalInteraction($body); + } + } + + function normalInteraction($body) { + // Priority 1: Navigation and progression buttons + const navButtons = $body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin"), button:contains("Submit"), button:contains("Done"), button:contains("Proceed")'); + if (navButtons.length > 0) { + console.log('๐Ÿ“ Clicking navigation button'); + cy.wrap(navButtons.first()).click(); + cy.wait(1000); + return; + } + + // Priority 2: Task-specific interactions + handleTaskSpecificInteractions($body); + } + + function thoroughInteraction($body) { + console.log('๐Ÿ” Thorough exploration mode'); + + // Try multiple navigation buttons + const allNavButtons = $body.find('button:contains("Continue"), button:contains("OK"), button:contains("Next"), button:contains("Start"), button:contains("Begin"), button:contains("Submit"), button:contains("Done"), button:contains("Try"), button:contains("Go")'); + if (allNavButtons.length > 0) { + allNavButtons.each((index, btn) => { + if (index < 2) { // Try first 2 buttons + cy.wrap(btn).click(); + cy.wait(500); + } + }); + return; + } + + handleTaskSpecificInteractions($body); + + // Try any enabled button + const enabledButtons = $body.find('button:not([disabled]):visible'); + if (enabledButtons.length > 0) { + cy.wrap(enabledButtons.first()).click(); + cy.wait(500); + } + } + + function alternativeInteraction($body) { + console.log('๐Ÿ”€ Alternative interaction mode'); + + // Look for different types of interactive elements + const selectors = [ + '[data-testid]', + '[data-cy]', + 'div[onclick]', + 'span[onclick]', + '.clickable', + '.interactive', + '[role="button"]', + 'a[href]:visible', + 'img[onclick]', + '[tabindex]:visible' + ]; + + for (const selector of selectors) { + const elements = $body.find(selector); + if (elements.length > 0) { + console.log(`๐ŸŽฏ Clicking ${selector}`); + cy.wrap(elements.first()).click(); + cy.wait(500); + break; + } + } + + handleTaskSpecificInteractions($body); + } + + function keyboardInteraction($body) { + console.log('โŒจ๏ธ Keyboard interaction mode'); + + // Try various keyboard inputs + const keys = ['{enter}', '{space}', '{tab}', '{rightarrow}', '{leftarrow}', '{downarrow}', '{uparrow}']; + const randomKey = keys[Math.floor(Math.random() * keys.length)]; + + cy.get('body').type(randomKey); + cy.wait(500); + + // For math/number tasks, try number keys + if ('TASK_KEBAB'.includes('math') || 'TASK_KEBAB'.includes('reasoning')) { + const numberKey = Math.floor(Math.random() * 10).toString(); + cy.get('body').type(numberKey); + cy.wait(500); + } + } + + function randomInteraction($body) { + console.log('๐ŸŽฒ Random exploration mode'); + + // Get all potentially interactive elements + const allInteractive = $body.find('button:visible, input:visible, select:visible, [onclick]:visible, [data-choice]:visible, .choice:visible, .option:visible'); + + if (allInteractive.length > 0) { + const randomElement = allInteractive.eq(Math.floor(Math.random() * allInteractive.length)); + console.log(`๐ŸŽฒ Random click on: ${randomElement.prop('tagName')}`); + cy.wrap(randomElement).click(); + cy.wait(1000); + } else { + // Click in different areas of the screen + const positions = [[200, 200], [400, 300], [600, 400], [300, 500]]; + const randomPos = positions[Math.floor(Math.random() * positions.length)]; + cy.get('body').click(randomPos[0], randomPos[1]); + cy.wait(500); + } + } + + function forceProgressionInteraction($body) { + console.log('๐Ÿš€ Force progression mode'); + + // Try clicking anywhere there might be hidden buttons + cy.get('body').click('center'); + cy.wait(500); + + // Try all buttons regardless of visibility + const allButtons = $body.find('button'); + if (allButtons.length > 0) { + allButtons.each((index, btn) => { + if (index < 3) { + cy.wrap(btn).click({ force: true }); + cy.wait(300); + } + }); + } + + // Try keyboard shortcuts that might advance the game + cy.get('body').type('{enter}'); + cy.wait(500); + cy.get('body').type(' '); // Spacebar + cy.wait(500); + } + + function handleTaskSpecificInteractions($body) { + // Task-specific interaction logic + + // Math tasks: number lines, input fields, multiple choice + if ('TASK_KEBAB'.includes('math') || 'TASK_KEBAB'.includes('egma')) { + // Number line sliders + const sliders = $body.find('input[type="range"], .slider input, .number-line input'); + if (sliders.length > 0) { + const randomValue = Math.floor(Math.random() * 100) + 1; + cy.wrap(sliders.first()).invoke('val', randomValue).trigger('input').trigger('change'); + cy.wait(500); + } + + // Number inputs + const numberInputs = $body.find('input[type="number"]'); + if (numberInputs.length > 0) { + const randomNumber = Math.floor(Math.random() * 20) + 1; + cy.wrap(numberInputs.first()).clear().type(randomNumber.toString()); + cy.wait(500); + } + } + + // Reasoning tasks: multiple choice, pattern selection + if ('TASK_KEBAB'.includes('reasoning') || 'TASK_KEBAB'.includes('matrix')) { + const choices = $body.find('[data-choice], .choice-button, .response-button, .answer-choice, .option, .choice'); + if (choices.length > 0) { + const randomIndex = Math.floor(Math.random() * choices.length); + cy.wrap(choices.eq(randomIndex)).click(); + cy.wait(500); + } + } + + // Memory tasks: card clicks, sequence interactions + if ('TASK_KEBAB'.includes('memory')) { + const cards = $body.find('.card, .memory-card, [data-card], .clickable-item'); + if (cards.length > 0) { + const randomCard = cards.eq(Math.floor(Math.random() * cards.length)); + cy.wrap(randomCard).click(); + cy.wait(500); + } + } + + // Audio tasks: play buttons, response buttons + const audioButtons = $body.find('button:contains("Play"), .play-button, .audio-button, button:contains("Listen")'); + if (audioButtons.length > 0) { + cy.wrap(audioButtons.first()).click(); + cy.wait(2000); // Wait for audio + } + + // General multiple choice + const generalChoices = $body.find('.choice, .option, .response, [data-option], .answer'); + if (generalChoices.length > 0) { + const randomChoice = generalChoices.eq(Math.floor(Math.random() * generalChoices.length)); + cy.wrap(randomChoice).click(); + cy.wait(500); + } + } + }); +}); +EOL + + # Replace placeholders + sed -i "s/TASK_NAME/${task_name}/g" "cypress/e2e/${task_kebab}_enhanced.cy.js" + sed -i "s/TASK_KEBAB/${task_kebab}/g" "cypress/e2e/${task_kebab}_enhanced.cy.js" + + echo "โœ… Created enhanced test for ${task_name}" +} + +# Create enhanced tests for all tasks +for task in "${TASKS[@]}"; do + # Convert to title case for display name + task_name=$(echo "$task" | sed 's/-/ /g' | sed 's/\b\w/\U&/g') + create_enhanced_test "$task_name" "$task" +done + +echo "" +echo "๐ŸŽ‰ All enhanced tests created!" +echo "๐Ÿ“Š Features added to each test:" +echo " โ€ข State-based loop detection" +echo " โ€ข Progressive interaction strategies (6 levels)" +echo " โ€ข Task-specific interaction logic" +echo " โ€ข Visited state tracking" +echo " โ€ข Intelligent escalation when stuck" +echo " โ€ข Enhanced logging and debugging" +echo "" +echo "๐Ÿš€ To test the enhanced EGMA version:" +echo " npx cypress run --spec \"cypress/e2e/egma-math_enhanced.cy.js\" --browser chrome --headless" \ No newline at end of file diff --git a/task-launcher/webpack.log b/task-launcher/webpack.log new file mode 100644 index 000000000..0489ad1de --- /dev/null +++ b/task-launcher/webpack.log @@ -0,0 +1,2 @@ +nohup: ignoring input +node: --experimental-memory-management= is not allowed in NODE_OPTIONS diff --git a/task-launcher/cypress/screenshots/memory_simple_complete.cy.js/001-01-initial-load.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-001-start.png similarity index 100% rename from task-launcher/cypress/screenshots/memory_simple_complete.cy.js/001-01-initial-load.png rename to usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-001-start.png diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-002-final.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-002-final.png new file mode 100644 index 000000000..90abe7306 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-002-final.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-003-step-1.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-003-step-1.png new file mode 100644 index 000000000..05f3b9cb8 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-003-step-1.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-004-step-2.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-004-step-2.png new file mode 100644 index 000000000..b4b5847fe Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-004-step-2.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-005-step-3.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-005-step-3.png new file mode 100644 index 000000000..c7583aa27 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-005-step-3.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-006-step-4.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-006-step-4.png new file mode 100644 index 000000000..acb28156a Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-006-step-4.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-007-step-5.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-007-step-5.png new file mode 100644 index 000000000..834e31df1 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-007-step-5.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-008-step-6.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-008-step-6.png new file mode 100644 index 000000000..314c935eb Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-008-step-6.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-009-step-7.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-009-step-7.png new file mode 100644 index 000000000..32891b164 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-009-step-7.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-010-step-8.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-010-step-8.png new file mode 100644 index 000000000..6c1391dc4 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-010-step-8.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-011-step-9.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-011-step-9.png new file mode 100644 index 000000000..06aa2ad42 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-011-step-9.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-012-step-10.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-012-step-10.png new file mode 100644 index 000000000..8c5e31398 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-012-step-10.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-013-step-11.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-013-step-11.png new file mode 100644 index 000000000..910bf601c Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-013-step-11.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-014-step-12.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-014-step-12.png new file mode 100644 index 000000000..f2427219d Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-014-step-12.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-015-step-13.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-015-step-13.png new file mode 100644 index 000000000..e0e209930 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-015-step-13.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-016-step-14.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-016-step-14.png new file mode 100644 index 000000000..5a825906c Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-016-step-14.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-017-step-15.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-017-step-15.png new file mode 100644 index 000000000..dd28977b0 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-017-step-15.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-018-step-16.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-018-step-16.png new file mode 100644 index 000000000..de7f1b532 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-018-step-16.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-019-step-17.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-019-step-17.png new file mode 100644 index 000000000..fc03b65bf Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-019-step-17.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-020-step-18.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-020-step-18.png new file mode 100644 index 000000000..57706370f Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-020-step-18.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-021-step-19.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-021-step-19.png new file mode 100644 index 000000000..8f48b6ef6 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-021-step-19.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-022-step-20.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-022-step-20.png new file mode 100644 index 000000000..8f398e6eb Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-022-step-20.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-023-step-21.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-023-step-21.png new file mode 100644 index 000000000..87b9b3809 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-023-step-21.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-024-step-22.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-024-step-22.png new file mode 100644 index 000000000..bb40a3d9b Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-024-step-22.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-025-step-23.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-025-step-23.png new file mode 100644 index 000000000..69656f5bf Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-025-step-23.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-026-step-24.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-026-step-24.png new file mode 100644 index 000000000..91c297c5c Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-026-step-24.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-027-step-25.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-027-step-25.png new file mode 100644 index 000000000..43fdb88b2 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-027-step-25.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-028-step-26.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-028-step-26.png new file mode 100644 index 000000000..25bf267a2 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-028-step-26.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-029-step-27.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-029-step-27.png new file mode 100644 index 000000000..2cb72ae96 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-029-step-27.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-030-step-28.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-030-step-28.png new file mode 100644 index 000000000..b470e861e Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-030-step-28.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-031-step-29.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-031-step-29.png new file mode 100644 index 000000000..04c02a80a Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-031-step-29.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-032-step-30.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-032-step-30.png new file mode 100644 index 000000000..948229e2b Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-032-step-30.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-033-step-31.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-033-step-31.png new file mode 100644 index 000000000..9d65dd114 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-033-step-31.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-034-step-32.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-034-step-32.png new file mode 100644 index 000000000..40eeef4ef Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-034-step-32.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-035-step-33.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-035-step-33.png new file mode 100644 index 000000000..e0d82ba18 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-035-step-33.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-036-step-34.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-036-step-34.png new file mode 100644 index 000000000..fbdc386c1 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-036-step-34.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-037-step-35.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-037-step-35.png new file mode 100644 index 000000000..7f837b2c8 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-037-step-35.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-038-step-36.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-038-step-36.png new file mode 100644 index 000000000..e563cc0dd Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-038-step-36.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-039-step-37.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-039-step-37.png new file mode 100644 index 000000000..88b7874f4 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-039-step-37.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-040-step-38.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-040-step-38.png new file mode 100644 index 000000000..07c280492 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-040-step-38.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-041-step-39.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-041-step-39.png new file mode 100644 index 000000000..18b5795e0 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-041-step-39.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-042-step-40.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-042-step-40.png new file mode 100644 index 000000000..80a0abec2 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-042-step-40.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-043-step-41.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-043-step-41.png new file mode 100644 index 000000000..60b6e4c55 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-043-step-41.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-044-step-42.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-044-step-42.png new file mode 100644 index 000000000..840da5d67 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-044-step-42.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-045-step-43.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-045-step-43.png new file mode 100644 index 000000000..2cf3d027a Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-045-step-43.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-046-step-44.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-046-step-44.png new file mode 100644 index 000000000..df8f438e5 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-046-step-44.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-047-step-45.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-047-step-45.png new file mode 100644 index 000000000..2c355d65f Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-047-step-45.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-048-step-46.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-048-step-46.png new file mode 100644 index 000000000..c5cbf95a4 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-048-step-46.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-049-step-47.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-049-step-47.png new file mode 100644 index 000000000..2842657cd Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-049-step-47.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-050-step-48.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-050-step-48.png new file mode 100644 index 000000000..3cbdd51ff Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-050-step-48.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-051-step-49.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-051-step-49.png new file mode 100644 index 000000000..12b0b4d62 Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-051-step-49.png differ diff --git a/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-052-step-50.png b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-052-step-50.png new file mode 100644 index 000000000..714f45b7e Binary files /dev/null and b/usable-screenshots/matrix-reasoning/2025-07-02T05-14-19-matrix-reasoning-052-step-50.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-001-initial-load.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-001-initial-load.png new file mode 100644 index 000000000..8f2d685a1 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-001-initial-load.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-002-task-started.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-002-task-started.png new file mode 100644 index 000000000..90abe7306 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-002-task-started.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-003-step-1.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-003-step-1.png new file mode 100644 index 000000000..6dd83fa6e Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-003-step-1.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-004-step-2.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-004-step-2.png new file mode 100644 index 000000000..1f47c4169 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-004-step-2.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-005-step-3.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-005-step-3.png new file mode 100644 index 000000000..05817b755 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-005-step-3.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-006-step-4.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-006-step-4.png new file mode 100644 index 000000000..b0edb969f Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-006-step-4.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-007-step-5.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-007-step-5.png new file mode 100644 index 000000000..3d74f4d6a Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-007-step-5.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-008-step-6.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-008-step-6.png new file mode 100644 index 000000000..185c76f71 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-008-step-6.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-009-step-7.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-009-step-7.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-009-step-7.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-010-step-8.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-010-step-8.png new file mode 100644 index 000000000..f59c10d58 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-010-step-8.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-011-step-9.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-011-step-9.png new file mode 100644 index 000000000..5aa17a440 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-011-step-9.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-012-step-10.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-012-step-10.png new file mode 100644 index 000000000..380e60782 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-012-step-10.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-013-step-11.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-013-step-11.png new file mode 100644 index 000000000..2b25ea51e Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-013-step-11.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-014-step-12.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-014-step-12.png new file mode 100644 index 000000000..a6a775e97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-014-step-12.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-015-step-13.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-015-step-13.png new file mode 100644 index 000000000..97abcd57f Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-015-step-13.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-016-step-14.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-016-step-14.png new file mode 100644 index 000000000..701b40578 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-016-step-14.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-017-step-15.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-017-step-15.png new file mode 100644 index 000000000..2ef5ad6b3 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-017-step-15.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-018-step-16.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-018-step-16.png new file mode 100644 index 000000000..9b2026bbd Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-018-step-16.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-019-step-17.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-019-step-17.png new file mode 100644 index 000000000..73bb14f49 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-019-step-17.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-020-step-18.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-020-step-18.png new file mode 100644 index 000000000..a3ff7aef9 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-020-step-18.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-021-step-19.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-021-step-19.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-021-step-19.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-022-step-20.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-022-step-20.png new file mode 100644 index 000000000..c85e7aac4 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-022-step-20.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-023-step-21.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-023-step-21.png new file mode 100644 index 000000000..f8d6871e0 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-023-step-21.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-024-step-22.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-024-step-22.png new file mode 100644 index 000000000..cc5e65cb4 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-024-step-22.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-025-step-23.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-025-step-23.png new file mode 100644 index 000000000..f92191e93 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-025-step-23.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-026-step-24.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-026-step-24.png new file mode 100644 index 000000000..49f2582f9 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-026-step-24.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-027-step-25.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-027-step-25.png new file mode 100644 index 000000000..554b345ca Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-027-step-25.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-028-step-26.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-028-step-26.png new file mode 100644 index 000000000..25a8173de Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-028-step-26.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-029-step-27.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-029-step-27.png new file mode 100644 index 000000000..420f03c1b Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-029-step-27.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-030-step-28.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-030-step-28.png new file mode 100644 index 000000000..d7f673a6e Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-030-step-28.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-031-step-29.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-031-step-29.png new file mode 100644 index 000000000..485068f41 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-031-step-29.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-032-step-30.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-032-step-30.png new file mode 100644 index 000000000..594fab0f0 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-032-step-30.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-033-step-31.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-033-step-31.png new file mode 100644 index 000000000..ce21979e6 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-033-step-31.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-034-step-32.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-034-step-32.png new file mode 100644 index 000000000..ee107b7ea Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-034-step-32.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-035-step-33.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-035-step-33.png new file mode 100644 index 000000000..09c458c64 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-035-step-33.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-036-step-34.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-036-step-34.png new file mode 100644 index 000000000..b72df483e Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-036-step-34.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-037-step-35.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-037-step-35.png new file mode 100644 index 000000000..d3419ad9d Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-037-step-35.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-038-step-36.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-038-step-36.png new file mode 100644 index 000000000..bf7a54dbe Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-038-step-36.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-039-step-37.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-039-step-37.png new file mode 100644 index 000000000..e25a099cb Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-039-step-37.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-040-step-38.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-040-step-38.png new file mode 100644 index 000000000..4be3227ee Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-040-step-38.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-041-step-39.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-041-step-39.png new file mode 100644 index 000000000..9e63ee501 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-041-step-39.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-042-step-40.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-042-step-40.png new file mode 100644 index 000000000..b01b3d209 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-042-step-40.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-043-step-41.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-043-step-41.png new file mode 100644 index 000000000..51c6027c5 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-043-step-41.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-044-step-42.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-044-step-42.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-044-step-42.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-045-step-43.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-045-step-43.png new file mode 100644 index 000000000..a664bf1f8 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-045-step-43.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-046-step-44.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-046-step-44.png new file mode 100644 index 000000000..9533deee5 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-046-step-44.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-047-step-45.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-047-step-45.png new file mode 100644 index 000000000..0f6fa740c Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-047-step-45.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-048-step-46.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-048-step-46.png new file mode 100644 index 000000000..549d0b558 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-048-step-46.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-049-step-47.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-049-step-47.png new file mode 100644 index 000000000..44446a8cc Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-049-step-47.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-050-step-48.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-050-step-48.png new file mode 100644 index 000000000..6b0271bd4 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-050-step-48.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-051-step-49.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-051-step-49.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-051-step-49.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-052-step-50.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-052-step-50.png new file mode 100644 index 000000000..b7dd4c69d Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-052-step-50.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-053-step-51.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-053-step-51.png new file mode 100644 index 000000000..0d2da9549 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-053-step-51.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-054-step-52.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-054-step-52.png new file mode 100644 index 000000000..884879d9c Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-054-step-52.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-055-step-53.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-055-step-53.png new file mode 100644 index 000000000..7bf689e57 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-055-step-53.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-056-step-54.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-056-step-54.png new file mode 100644 index 000000000..e07c1fb1b Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-056-step-54.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-057-step-55.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-057-step-55.png new file mode 100644 index 000000000..aa7eba0e3 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-057-step-55.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-058-step-56.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-058-step-56.png new file mode 100644 index 000000000..9cd5ea277 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-058-step-56.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-059-step-57.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-059-step-57.png new file mode 100644 index 000000000..0a305e821 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-059-step-57.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-060-step-58.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-060-step-58.png new file mode 100644 index 000000000..17c48599d Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-060-step-58.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-061-step-59.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-061-step-59.png new file mode 100644 index 000000000..05ad4be50 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-061-step-59.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-062-step-60.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-062-step-60.png new file mode 100644 index 000000000..8e35ef6d1 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-062-step-60.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-063-step-61.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-063-step-61.png new file mode 100644 index 000000000..96bc7d73f Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-063-step-61.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-064-step-62.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-064-step-62.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-064-step-62.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-065-step-63.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-065-step-63.png new file mode 100644 index 000000000..b886aa8bc Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-065-step-63.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-066-step-64.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-066-step-64.png new file mode 100644 index 000000000..7b3186349 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-066-step-64.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-067-step-65.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-067-step-65.png new file mode 100644 index 000000000..b787b9910 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-067-step-65.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-068-step-66.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-068-step-66.png new file mode 100644 index 000000000..b32578f9a Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-068-step-66.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-069-step-67.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-069-step-67.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-069-step-67.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-070-step-68.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-070-step-68.png new file mode 100644 index 000000000..3d30aebe3 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-070-step-68.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-071-step-69.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-071-step-69.png new file mode 100644 index 000000000..399094043 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-071-step-69.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-072-step-70.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-072-step-70.png new file mode 100644 index 000000000..6cb2390ad Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-072-step-70.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-073-step-71.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-073-step-71.png new file mode 100644 index 000000000..29f6a7af9 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-073-step-71.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-074-step-72.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-074-step-72.png new file mode 100644 index 000000000..acb590b97 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-074-step-72.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-075-step-73.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-075-step-73.png new file mode 100644 index 000000000..c112bfd3b Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-075-step-73.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-076-step-74.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-076-step-74.png new file mode 100644 index 000000000..d3248a30b Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-076-step-74.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-077-step-75.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-077-step-75.png new file mode 100644 index 000000000..242415193 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-077-step-75.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-078-step-76.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-078-step-76.png new file mode 100644 index 000000000..0c5256e60 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-078-step-76.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-079-step-77.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-079-step-77.png new file mode 100644 index 000000000..9156aa0a1 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-079-step-77.png differ diff --git a/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-080-task-completed.png b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-080-task-completed.png new file mode 100644 index 000000000..90abe7306 Binary files /dev/null and b/usable-screenshots/theory-of-mind/2025-07-02T04-13-18-theory-of-mind-080-task-completed.png differ