leptos-motion-core 0.3.3

Core animation engine for Leptos Motion
Documentation
//! TDD tests for WASM browser environment
//!
//! These tests ensure that the animation system works correctly in the browser

use crate::*;
use std::collections::HashMap;

// WASM-specific test configuration - conditional compilation
#[cfg(feature = "web-sys")]
use wasm_bindgen_test::*;

#[cfg(feature = "web-sys")]
wasm_bindgen_test_configure!(run_in_browser);

/// Test that basic animation types work in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_basic_animation_types() {
    // Test AnimationValue creation
    let number_value = AnimationValue::Number(42.0);
    let string_value = AnimationValue::String("test".to_string());

    assert!(matches!(number_value, AnimationValue::Number(42.0)));
    assert!(matches!(string_value, AnimationValue::String(ref s) if s == "test"));

    // Test AnimationHandle creation
    let handle = AnimationHandle(123);
    assert_eq!(handle.0, 123);

    // Test Transition creation
    let transition = Transition {
        duration: Some(1.0),
        ease: Easing::Linear,
        delay: Some(0.1),
        repeat: RepeatConfig::Never,
        stagger: None,
    };

    assert_eq!(transition.duration, Some(1.0));
    assert_eq!(transition.ease, Easing::EaseInOut);
    assert_eq!(transition.delay, Some(0.1));
}

/// Test that animation targets work in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_targets() {
    let mut target = HashMap::new();
    target.insert("opacity".to_string(), AnimationValue::Number(1.0));
    target.insert("scale".to_string(), AnimationValue::Number(1.2));
    target.insert("rotate".to_string(), AnimationValue::Number(45.0));

    assert_eq!(target.len(), 3);
    assert!(target.contains_key("opacity"));
    assert!(target.contains_key("scale"));
    assert!(target.contains_key("rotate"));

    // Test getting values
    if let Some(AnimationValue::Number(opacity)) = target.get("opacity") {
        assert_eq!(*opacity, 1.0);
    } else {
        panic!("Expected opacity to be a number");
    }

    if let Some(AnimationValue::Number(scale)) = target.get("scale") {
        assert_eq!(*scale, 1.2);
    } else {
        panic!("Expected scale to be a number");
    }
}

/// Test that animation engines can be created in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_engines() {
    // Test MinimalEngine creation
    let minimal_engine = MinimalEngine::new();
    assert_eq!(minimal_engine.active_count(), 0);

    // Test SimplifiedAnimationEngine creation
    let simplified_engine = SimplifiedAnimationEngine::new();
    // The engine should be created successfully

    // Test that engines can be used
    let target = {
        let mut target = HashMap::new();
        target.insert("opacity".to_string(), AnimationValue::Number(1.0));
        target
    };

    let transition = Transition {
        duration: Some(0.5),
        ease: Easing::EaseInOut,
        delay: Some(0.0),
        repeat: RepeatConfig::Never,
        stagger: None,
    };

    // Test that we can create animation configurations
    assert_eq!(target.len(), 1);
    assert_eq!(transition.duration, Some(0.5));
}

/// Test that error handling works in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_error_handling() {
    let handler = DefaultErrorHandler::default();

    // Test engine unavailable error
    let engine_error = AnimationError::EngineUnavailable("WAAPI not supported".to_string());
    let context = ErrorContext::new("animate")
        .with_component("MotionDiv")
        .with_info("property", "opacity");

    let strategy = handler.handle_error(&engine_error, &context);
    assert_eq!(strategy, RecoveryStrategy::Fallback);

    // Test invalid property error
    let property_error = AnimationError::InvalidProperty {
        property: "invalid_prop".to_string(),
    };
    let strategy = handler.handle_error(&property_error, &context);
    assert_eq!(strategy, RecoveryStrategy::Skip);

    // Test user-friendly error messages
    let mut handler_with_messages = DefaultErrorHandler {
        log_errors: false,
        show_user_messages: true,
    };

    let message = handler_with_messages.user_message(&engine_error);
    assert_eq!(message, "Animation system temporarily unavailable");
}

/// Test that performance monitoring works in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_performance_monitoring() {
    // Test that we can create performance monitoring structures
    let max_frame_time = 16.0;
    let max_memory = 100.0;
    let max_animations = 50;

    // Test that performance values are reasonable
    assert!(max_frame_time > 0.0);
    assert!(max_memory > 0.0);
    assert!(max_animations > 0);

    // Test that we can create performance metrics manually
    let frame_time_ms = 12.0;
    let memory_usage_mb = 50.0;
    let active_animations = 5;
    let dropped_frames = 0;
    let total_frames = 100;

    assert_eq!(frame_time_ms, 12.0);
    assert_eq!(memory_usage_mb, 50.0);
    assert_eq!(active_animations, 5);
    assert_eq!(dropped_frames, 0);
    assert_eq!(total_frames, 100);
}

/// Test that animation presets work in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_presets() {
    // Test that we can create animation presets manually
    let mut fade_in_config = HashMap::new();
    fade_in_config.insert("opacity".to_string(), AnimationValue::Number(1.0));

    let mut slide_up_config = HashMap::new();
    slide_up_config.insert("y".to_string(), AnimationValue::Number(0.0));

    let mut scale_in_config = HashMap::new();
    scale_in_config.insert("scale".to_string(), AnimationValue::Number(1.0));

    // Test that presets are properly defined
    assert!(fade_in_config.contains_key("opacity"));
    assert!(slide_up_config.contains_key("y"));
    assert!(scale_in_config.contains_key("scale"));

    // Test that we can get preset configurations
    if let Some(AnimationValue::Number(opacity)) = fade_in_config.get("opacity") {
        assert_eq!(*opacity, 1.0);
    }

    if let Some(AnimationValue::Number(y)) = slide_up_config.get("y") {
        assert_eq!(*y, 0.0);
    }

    if let Some(AnimationValue::Number(scale)) = scale_in_config.get("scale") {
        assert_eq!(*scale, 1.0);
    }
}

/// Test that motion values work in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_motion_values() {
    // Test MotionNumber
    let motion_number = MotionNumber::new(42.0);
    assert_eq!(motion_number.get(), 42.0);

    // Test MotionTransform
    let motion_transform = MotionTransform::identity();
    let transform = motion_transform.get();
    assert!(transform.is_identity());

    // Test that motion values can be updated
    motion_number.set(84.0);
    assert_eq!(motion_number.get(), 84.0);

    // Test that we can create string values manually
    let test_string = "test".to_string();
    assert_eq!(test_string, "test");

    let updated_string = "updated".to_string();
    assert_eq!(updated_string, "updated");
}

/// Test that animation interpolation works in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_interpolation() {
    // Test that we can create interpolation values
    let start = 0.0;
    let end = 100.0;
    let progress = 0.5;

    // Test manual linear interpolation
    let interpolated = start + (end - start) * progress;
    assert_eq!(interpolated, 50.0);

    // Test that interpolation handles edge cases
    let zero_progress = start + (end - start) * 0.0;
    assert_eq!(zero_progress, start);

    let full_progress = start + (end - start) * 1.0;
    assert_eq!(full_progress, end);
}

/// Test that animation timing works in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_timing() {
    // Test that we can create timing configurations
    let transition = Transition {
        duration: Some(1.0),
        delay: Some(0.1),
        ease: Easing::EaseInOut,
        repeat: RepeatConfig::Never,
        stagger: None,
    };

    assert_eq!(transition.duration, Some(1.0));
    assert_eq!(transition.delay, Some(0.1));
    assert_eq!(transition.ease, Easing::EaseInOut);

    // Test that we can calculate animation progress manually
    let start_time = 0.0;
    let current_time = 0.5;
    let duration = 1.0;

    let progress = (current_time - start_time) / duration;
    assert_eq!(progress, 0.5);

    // Test that progress is clamped to [0, 1]
    let negative_progress: f64 = (-0.5 - start_time) / duration;
    let clamped_negative = negative_progress.max(0.0).min(1.0);
    assert_eq!(clamped_negative, 0.0);

    let overflow_progress: f64 = (2.0 - start_time) / duration;
    let clamped_overflow = overflow_progress.max(0.0).min(1.0);
    assert_eq!(clamped_overflow, 1.0);
}

/// Test that animation cleanup works in WASM environment
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_animation_cleanup() {
    // Test that we can create and clean up animations
    let engine = MinimalEngine::new();
    let initial_count = engine.active_count();

    // Test that we can handle cleanup gracefully
    // Note: MinimalEngine doesn't have a cleanup method, so we test the active count
    assert_eq!(engine.active_count(), initial_count);

    // Test that we can handle cleanup errors gracefully
    let result = std::panic::catch_unwind(|| {
        // This should not panic
        let _ = engine.active_count();
    });

    assert!(result.is_ok());
}

/// Test that animation system works with real browser APIs
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_browser_integration() {
    // Test that we can access browser performance API
    let performance = web_sys::window().unwrap().performance().unwrap();

    let now = performance.now();
    assert!(now >= 0.0);

    // Test that we can create DOM elements
    let document = web_sys::window().unwrap().document().unwrap();

    let element = document.create_element("div").unwrap();
    assert!(element.tag_name() == "DIV");

    // Test that we can set element styles
    element
        .set_attribute("style", "opacity: 0.5; transform: scale(1.2);")
        .unwrap();

    let style = element.get_attribute("style").unwrap();
    assert!(style.contains("opacity: 0.5"));
    assert!(style.contains("transform: scale(1.2)"));
}

/// Test that animation system handles browser limitations gracefully
#[cfg_attr(feature = "web-sys", wasm_bindgen_test)]
#[cfg_attr(not(feature = "web-sys"), test)]
fn test_wasm_browser_limitations() {
    // Test that we can handle missing browser APIs
    let result = std::panic::catch_unwind(|| {
        // This should not panic even if APIs are missing
        let _ = web_sys::window();
    });

    assert!(result.is_ok());

    // Test that we can handle browser errors gracefully
    let handler = DefaultErrorHandler::default();
    let dom_error = AnimationError::DomError("Element not found".to_string());
    let context = ErrorContext::new("animate");

    let strategy = handler.handle_error(&dom_error, &context);
    assert_eq!(strategy, RecoveryStrategy::Retry);

    // Test that we can handle performance limitations
    let perf_error = AnimationError::PerformanceBudgetExceeded("Frame budget exceeded".to_string());
    let strategy = handler.handle_error(&perf_error, &context);
    assert_eq!(strategy, RecoveryStrategy::Fallback);
}