use std::process::Command;
use serde_json::Value;
mod common;
use common::TestBrowser;
fn binary() -> String {
let mut path = std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
path.push("chrome-agent");
path.to_string_lossy().into_owned()
}
fn run_cli(args: &[&str]) -> (String, i32) {
let output = Command::new(binary()).args(args).output().expect("Failed to run chrome-agent");
(
String::from_utf8_lossy(&output.stdout).to_string(),
output.status.code().unwrap_or(-1),
)
}
fn open(browser: &str) -> bool {
if !common::browser_ready() {
return false;
}
let (_, code) = run_cli(&["--browser", browser, "goto", &common::fixture_url("checkable_kinds.html")]);
if code != 0 {
return common::unavailable("goto checkable_kinds.html failed");
}
true
}
fn eval(browser: &str, expr: &str) -> String {
let (out, _) = run_cli(&["--browser", browser, "eval", expr]);
out.trim().trim_matches('"').to_string()
}
fn check(browser: &str, selector: &str) -> (Value, i32) {
let (out, code) = run_cli(&[
"--browser", browser, "--verdict", "off", "--json", "check", "--selector", selector,
]);
(serde_json::from_str(&out).unwrap_or(Value::Null), code)
}
#[test]
fn checking_an_aria_checkbox_that_is_already_on_leaves_it_on() {
let b = TestBrowser::new("chk-aria-on");
if !open(b.name()) {
return;
}
assert_eq!(eval(b.name(), "document.getElementById('aria_on').getAttribute('aria-checked')"), "true");
let (v, code) = check(b.name(), "#aria_on");
assert_eq!(code, 0, "check should succeed: {v}");
assert_eq!(
eval(b.name(), "document.getElementById('aria_on').getAttribute('aria-checked')"),
"true",
"check must never turn a checked box off: {v}"
);
assert!(
v["message"].as_str().unwrap_or_default().contains("Already"),
"an already-checked box should report as already checked: {v}"
);
}
#[test]
fn checking_an_aria_checkbox_that_is_off_turns_it_on() {
let b = TestBrowser::new("chk-aria-off");
if !open(b.name()) {
return;
}
let (v, code) = check(b.name(), "#aria_off");
assert_eq!(code, 0, "check should succeed: {v}");
assert_eq!(
eval(b.name(), "document.getElementById('aria_off').getAttribute('aria-checked')"),
"true",
"check should have turned it on: {v}"
);
}
#[test]
fn checking_a_text_input_is_refused() {
let b = TestBrowser::new("chk-text");
if !open(b.name()) {
return;
}
let (v, code) = check(b.name(), "#text");
assert_ne!(code, 0, "checking a text input should fail, got: {v}");
assert_eq!(v["ok"], false, "{v}");
let err = v["error"].as_str().unwrap_or_default();
assert!(
err.contains("checkbox") || err.contains("checkable") || err.contains("radio"),
"the error should say what kind of element is required: {v}"
);
}
#[test]
fn unchecking_a_radio_is_refused() {
let b = TestBrowser::new("chk-radio");
if !open(b.name()) {
return;
}
let (out, code) = run_cli(&[
"--browser", b.name(), "--verdict", "off", "--json", "uncheck", "--selector", "#radio",
]);
let v: Value = serde_json::from_str(&out).unwrap_or(Value::Null);
assert_ne!(code, 0, "unchecking a radio should fail, got: {v}");
assert_eq!(eval(b.name(), "document.getElementById('radio').checked"), "true", "still checked: {v}");
assert!(
v["error"].as_str().unwrap_or_default().contains("radio cannot be unchecked"),
"the reason must name the real constraint, not just report the click did nothing: {v}"
);
}
#[test]
fn native_checkboxes_still_work() {
let b = TestBrowser::new("chk-native");
if !open(b.name()) {
return;
}
let (v, code) = check(b.name(), "#native");
assert_eq!(code, 0, "{v}");
assert_eq!(eval(b.name(), "document.getElementById('native').checked"), "true", "{v}");
let (v, code) = check(b.name(), "#native_on");
assert_eq!(code, 0, "{v}");
assert_eq!(eval(b.name(), "document.getElementById('native_on').checked"), "true", "already on stays on: {v}");
assert!(v["message"].as_str().unwrap_or_default().contains("Already"), "{v}");
}