#![cfg(feature = "browser")]
use crate::brain::tools::browser::{build_inventory_js, inventory_header};
#[test]
fn inventory_targets_interactive_element_union() {
let js = build_inventory_js(50);
assert!(js.contains("a[href]"), "links");
assert!(js.contains("button"), "buttons");
assert!(
js.contains("input:not([type=\"hidden\"])"),
"visible inputs"
);
assert!(js.contains("select"), "selects");
assert!(js.contains("textarea"), "textareas");
assert!(js.contains("summary"), "disclosure summaries");
assert!(js.contains("[role=\"button\"]"), "ARIA buttons");
assert!(js.contains("[role=\"link\"]"), "ARIA links");
assert!(js.contains("[role=\"checkbox\"]"), "ARIA checkboxes");
assert!(js.contains("[role=\"tab\"]"), "ARIA tabs");
assert!(js.contains("[role=\"menuitem\"]"), "ARIA menu items");
assert!(js.contains("[role=\"option\"]"), "ARIA options");
assert!(js.contains("[contenteditable=\"true\"]"), "contenteditable");
assert!(js.contains("[tabindex]:not([tabindex=\"-1\"])"), "tabindex");
}
#[test]
fn inventory_pre_filters_to_visible_elements() {
let js = build_inventory_js(50);
assert!(js.contains("getBoundingClientRect()"));
assert!(js.contains("rect.width > 0"));
assert!(js.contains("rect.height > 0"));
assert!(js.contains("getComputedStyle(el).visibility !== 'hidden'"));
assert!(js.contains("getComputedStyle(el).display !== 'none'"));
}
#[test]
fn inventory_respects_the_limit() {
let js = build_inventory_js(40);
assert!(js.contains("visible.length >= 40"));
assert!(js.contains("break"));
}
#[test]
fn inventory_uses_shared_match_index_serializer() {
let js = build_inventory_js(50);
assert!(
js.contains("removeAttribute('data-opencrabs-match')"),
"must clear stale match attributes before re-indexing"
);
assert!(
js.contains(r#"selector: '[data-opencrabs-match="' + i + '"]'"#),
"must return the stable indexed selector shape"
);
assert!(js.contains("text:"));
assert!(js.contains("tag:"));
assert!(js.contains("visible:"));
}
#[test]
fn inventory_has_no_user_supplied_string_to_escape() {
let js = build_inventory_js(10);
assert!(
!js.contains(r#""+ "#),
"no concatenation into the selector string"
);
assert!(js.contains("querySelectorAll(sel)"));
}
#[test]
fn inventory_dedups_generic_wrappers_contained_in_accepted_elements() {
let js = build_inventory_js(50);
assert!(js.contains("acceptedRects"), "containment tracker present");
assert!(
js.contains("acceptedRects.some(r =>"),
"containment check against already-accepted rects"
);
assert!(js.contains("r.left - 1 <= rect.left"), "left tolerance");
assert!(js.contains("rect.right <= r.right + 1"), "right tolerance");
assert!(js.contains("r.top - 1 <= rect.top"), "top tolerance");
assert!(
js.contains("rect.bottom <= r.bottom + 1"),
"bottom tolerance"
);
assert!(js.contains("collapsed++"), "collapse counter incremented");
}
#[test]
fn inventory_dedup_preserves_own_semantics_elements() {
let js = build_inventory_js(50);
let own_pos = js
.find("const own = el.tagName === 'INPUT'")
.expect("own-semantics classification present");
let dup_pos = js
.find("const dup = acceptedRects.some")
.expect("containment check present");
let gate_pos = js.find("if (!own)").expect("gate present");
assert!(
own_pos < gate_pos && gate_pos < dup_pos,
"classification → gate → containment, in that order"
);
for token in [
"'INPUT'",
"'SELECT'",
"'TEXTAREA'",
"'SUMMARY'",
"'checkbox'",
"'menuitem'",
"'tab'",
"'option'",
"isContentEditable",
] {
assert!(js.contains(token), "own-semantics set includes {token}");
}
}
#[test]
fn inventory_attaches_collapse_count_for_header() {
let js = build_inventory_js(50);
assert!(js.contains("visible.collapsed = collapsed"));
assert!(js.contains("visible.occluded = occluded"));
assert!(js.contains("typeof nodes.collapsed === 'number'"));
assert!(js.contains("typeof nodes.occluded === 'number'"));
assert!(js.contains("document.elementFromPoint(cx, cy)"));
assert!(js.contains("Math.min(Math.max(rect.left + rect.width / 2, 0), vw - 1)"));
assert!(js.contains("hit !== el && !el.contains(hit)"));
assert!(js.contains("occluded++; continue;"));
assert!(js.contains("items: out"));
}
#[test]
fn inventory_header_mentions_collapsed_count_only_when_nonzero() {
let plain = inventory_header(12, 0, 0);
assert!(plain.starts_with("12 visible interactive elements on this page"));
assert!(!plain.contains("collapsed"), "no collapse note at zero");
assert!(!plain.contains("occluded"), "no occlusion note at zero");
let multi = inventory_header(9, 3, 0);
assert!(multi.contains("(3 nested duplicates collapsed)"), "{multi}");
let single = inventory_header(5, 1, 0);
assert!(
single.contains("(1 nested duplicate collapsed)"),
"{single}"
);
assert!(plain.contains("[data-opencrabs-match=\"N\"]"));
assert!(multi.contains("[data-opencrabs-match=\"N\"]"));
}
#[test]
fn inventory_header_mentions_occluded_count_only_when_nonzero() {
let occluded_only = inventory_header(7, 0, 2);
assert!(
occluded_only.contains(", 2 hidden (occluded)"),
"{occluded_only}"
);
assert!(!occluded_only.contains("collapsed"));
let both = inventory_header(6, 1, 4);
assert!(both.contains("(1 nested duplicate collapsed)"), "{both}");
assert!(both.contains(", 4 hidden (occluded)"), "{both}");
let one = inventory_header(3, 0, 1);
assert!(one.contains(", 1 hidden (occluded)"), "{one}");
}