Locators

Probar provides Playwright-style locators for finding game elements.

Basic Locators

#![allow(unused)]
fn main() {
use jugar_probar::locator::*;

// By ID
let player = Locator::id("player");

// By name
let score = Locator::name("score_display");

// By component type
let balls = Locator::component::<Ball>();

// By tag
let enemies = Locator::tag("enemy");
}

Entity Queries

#![allow(unused)]
fn main() {
let platform = WebPlatform::new_for_test(config);

// Find single entity
let player = platform.locate(Locator::id("player"))?;
let pos = platform.get_position(player);

// Find all matching
let coins: Vec<Entity> = platform.locate_all(Locator::tag("coin"));
assert_eq!(coins.len(), 5);

// First matching
let first_enemy = platform.locate_first(Locator::tag("enemy"));
}

Compound Locators

#![allow(unused)]
fn main() {
// AND - must match all
let armed_enemy = Locator::tag("enemy")
    .and(Locator::has_component::<Weapon>());

// OR - match any
let interactable = Locator::tag("door")
    .or(Locator::tag("chest"));

// NOT - exclude
let non_player = Locator::component::<Character>()
    .not(Locator::id("player"));
}

Spatial Locators

#![allow(unused)]
fn main() {
// Within radius
let nearby = Locator::within_radius(player_pos, 100.0);

// In bounds
let visible = Locator::in_bounds(screen_bounds);

// Nearest to point
let closest_enemy = Locator::nearest_to(player_pos)
    .with_filter(Locator::tag("enemy"));
}

Component-Based Locators

#![allow(unused)]
fn main() {
// Has specific component
let physics_entities = Locator::has_component::<RigidBody>();

// Component matches predicate
let low_health = Locator::component_matches::<Health>(|h| h.value < 20);

// Has all components
let complete_entities = Locator::has_all_components::<(
    Position,
    Velocity,
    Sprite,
)>();
}

Type-Safe Locators (with derive)

Using jugar-probar-derive for compile-time checked selectors:

#![allow(unused)]
fn main() {
use jugar_probar_derive::Entity;

#[derive(Entity)]
#[entity(id = "player")]
struct Player;

#[derive(Entity)]
#[entity(tag = "enemy")]
struct Enemy;

// Compile-time verified
let player = platform.locate::<Player>()?;
let enemies = platform.locate_all::<Enemy>();
}

Waiting for Elements

#![allow(unused)]
fn main() {
// Wait for entity to exist
let boss = platform.wait_for(
    Locator::id("boss"),
    Duration::from_secs(5),
)?;

// Wait for condition
platform.wait_until(
    || platform.locate(Locator::id("door")).is_some(),
    Duration::from_secs(2),
)?;
}

Locator Chains

#![allow(unused)]
fn main() {
// Find children
let player_weapon = Locator::id("player")
    .child(Locator::tag("weapon"));

// Find parent
let weapon_owner = Locator::id("sword")
    .parent();

// Find siblings
let adjacent_tiles = Locator::id("current_tile")
    .siblings();
}

Actions on Located Elements

#![allow(unused)]
fn main() {
let button = platform.locate(Locator::id("start_button"))?;

// Get info
let pos = platform.get_position(button);
let bounds = platform.get_bounds(button);
let visible = platform.is_visible(button);

// Interact
platform.click(button);
platform.hover(button);

// Check state
let enabled = platform.is_enabled(button);
let focused = platform.is_focused(button);
}

Example Test

#![allow(unused)]
fn main() {
#[test]
fn test_coin_collection() {
    let mut platform = WebPlatform::new_for_test(config);

    // Count initial coins
    let initial_coins = platform.locate_all(Locator::tag("coin")).len();
    assert_eq!(initial_coins, 5);

    // Move player to first coin
    let first_coin = platform.locate_first(Locator::tag("coin")).unwrap();
    let coin_pos = platform.get_position(first_coin);

    // Simulate movement
    move_player_to(&mut platform, coin_pos);

    // Coin should be collected
    let remaining_coins = platform.locate_all(Locator::tag("coin")).len();
    assert_eq!(remaining_coins, 4);

    // Score should increase
    let score_display = platform.locate(Locator::id("score")).unwrap();
    let score_text = platform.get_text(score_display);
    assert!(score_text.contains("10"));
}
}