use crate::core::engine::{EngineAdapter, EngineError};
#[derive(Debug, Clone)]
pub struct VisibilityOptions {
pub selector: String,
pub timeout_ms: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct VisibilityResult {
pub visible: bool,
pub exists: bool,
pub in_viewport: bool,
}
pub async fn is_visible(adapter: &dyn EngineAdapter, selector: &str) -> Result<bool, EngineError> {
adapter.is_visible(selector).await
}
pub async fn is_enabled(adapter: &dyn EngineAdapter, selector: &str) -> Result<bool, EngineError> {
adapter.is_enabled(selector).await
}
pub async fn count(adapter: &dyn EngineAdapter, selector: &str) -> Result<usize, EngineError> {
adapter.count(selector).await
}
pub fn is_in_viewport(
bounding_box: (f64, f64, f64, f64),
viewport_width: f64,
viewport_height: f64,
margin: f64,
) -> bool {
let (x, y, width, height) = bounding_box;
let in_vertical = y < viewport_height - margin && (y + height) > margin;
let in_horizontal = x < viewport_width - margin && (x + width) > margin;
in_vertical && in_horizontal
}
pub fn needs_scrolling(
bounding_box: (f64, f64, f64, f64),
viewport_height: f64,
threshold_percent: f64,
) -> bool {
let (_, y, _, height) = bounding_box;
let element_center = y + height / 2.0;
let viewport_center = viewport_height / 2.0;
let distance_from_center = (element_center - viewport_center).abs();
let threshold_pixels = (viewport_height * threshold_percent) / 100.0;
let is_visible = y >= 0.0 && (y + height) <= viewport_height;
let is_within_threshold = distance_from_center <= threshold_pixels;
!is_visible || !is_within_threshold
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_in_viewport_fully_visible() {
assert!(is_in_viewport(
(100.0, 100.0, 50.0, 50.0),
800.0,
600.0,
0.0
));
}
#[test]
fn is_in_viewport_partially_visible() {
assert!(is_in_viewport(
(60.0, 100.0, 50.0, 50.0),
800.0,
600.0,
50.0
));
assert!(is_in_viewport(
(-10.0, 100.0, 50.0, 50.0),
800.0,
600.0,
0.0
));
}
#[test]
fn is_in_viewport_not_visible() {
assert!(!is_in_viewport(
(100.0, -200.0, 50.0, 50.0),
800.0,
600.0,
50.0
));
assert!(!is_in_viewport(
(100.0, 700.0, 50.0, 50.0),
800.0,
600.0,
50.0
));
}
#[test]
fn needs_scrolling_element_centered() {
let viewport_height = 600.0;
let element_y = 270.0; let element_height = 60.0;
assert!(!needs_scrolling(
(0.0, element_y, 100.0, element_height),
viewport_height,
10.0
));
}
#[test]
fn needs_scrolling_element_at_top() {
let viewport_height = 600.0;
assert!(needs_scrolling(
(0.0, 10.0, 100.0, 50.0),
viewport_height,
10.0
));
}
#[test]
fn needs_scrolling_element_at_bottom() {
let viewport_height = 600.0;
assert!(needs_scrolling(
(0.0, 540.0, 100.0, 50.0),
viewport_height,
10.0
));
}
#[test]
fn needs_scrolling_element_outside_viewport() {
let viewport_height = 600.0;
assert!(needs_scrolling(
(0.0, 700.0, 100.0, 50.0),
viewport_height,
10.0
));
}
}