Files
Ely-GPUI-Components/src/misc/tests/codes.rs
T
ZacharyZhang-NYandClaude Opus 5.5 19a5295ed8 T42c Misc · Codes and checks passes review
Tests now hold focus in the field after an answer and a passed captcha with no Tab stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:22:34 -04:00

200 lines
6.3 KiB
Rust

use std::{
cell::{Cell, RefCell},
sync::Arc,
};
use gpui::{AnyElement, IntoElement, RenderImage, TestAppContext, VisualTestContext};
use super::{at_root, press, settle, shown, stage};
use crate::misc::{Captcha, CaptchaState, QrCodeScanner, scanner::frame_of};
thread_local! {
static FRAME: RefCell<Option<Arc<RenderImage>>> = const { RefCell::new(None) };
static SAID: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
static STATE: Cell<CaptchaState> = const { Cell::new(CaptchaState::Asking) };
}
fn said() -> Vec<String> {
SAID.with(|said| said.borrow().clone())
}
fn say(text: &str) {
SAID.with(|said| said.borrow_mut().push(text.to_string()));
}
/// Hands the stage a new frame, or state, and lets it draw and read.
fn show(cx: &mut VisualTestContext) {
cx.update(|window, _| window.refresh());
settle(cx);
}
fn scanner() -> AnyElement {
let scanner = QrCodeScanner::new("scanner", 1.0).on_scan(|text, _, _| say(text));
match FRAME.with(|frame| frame.borrow().clone()) {
Some(frame) => scanner.frame(frame),
None => scanner,
}
.into_any_element()
}
/// Frames read off the main thread; a code goes to the owner once, and a new one after it.
#[gpui::test]
fn a_scanner_reads_each_code_once(cx: &mut TestAppContext) {
FRAME.with(|frame| frame.replace(Some(frame_of("first"))));
let (_, cx) = stage(scanner, cx);
assert_eq!(said(), ["first"]);
assert!(shown("scanned-first", cx));
FRAME.with(|frame| frame.replace(Some(frame_of("first"))));
show(cx);
assert_eq!(
said(),
["first"],
"a new frame of the same code says nothing"
);
FRAME.with(|frame| frame.replace(Some(frame_of("second"))));
show(cx);
assert_eq!(said(), ["first", "second"]);
}
/// A frame that comes while one is read waits: the read's end redraws, and the newest frame is read next.
#[gpui::test]
fn a_frame_that_comes_mid_read_is_read_next(cx: &mut TestAppContext) {
let (_, cx) = stage(scanner, cx);
for text in ["first", "second"] {
FRAME.with(|frame| frame.replace(Some(frame_of(text))));
cx.update(|window, _| window.refresh());
}
assert!(said().is_empty(), "both frames drew before any read ended");
cx.run_until_parked();
assert_eq!(said(), ["first", "second"], "only the read's end redrew");
}
fn captcha() -> AnyElement {
Captcha::new("captcha", frame_of("W7XK"), 1.0)
.state(STATE.get())
.on_answer(|text, _, _| say(text))
.into_any_element()
}
/// Enter hands the answer over and empties the field, which keeps focus through the owner's verdict.
#[gpui::test]
fn a_captcha_hands_over_answers_and_keeps_focus(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
press("enter", cx);
assert!(said().is_empty(), "nothing typed, nothing handed over");
cx.simulate_input(" w7xk ");
press("enter", cx);
assert_eq!(said(), ["w7xk"]);
STATE.set(CaptchaState::Wrong);
show(cx);
assert!(shown("captcha-wrong", cx));
cx.simulate_input("W7XK");
press("enter", cx);
assert_eq!(
said(),
["w7xk", "W7XK"],
"the field emptied for the next try"
);
STATE.set(CaptchaState::Passed);
show(cx);
assert!(
shown("captcha-verified-focused", cx),
"the resting field handed its focus to the mark"
);
cx.simulate_input("again");
press("enter", cx);
assert_eq!(said().len(), 2, "a passed captcha takes no more answers");
}
/// With the field emptied, verdicts and a new ask turn Verify and its mark into each other on one focus handle.
#[gpui::test]
fn the_owners_verdicts_leave_focus_in_place(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
cx.simulate_input("W7XK");
press("enter", cx);
STATE.set(CaptchaState::Checking);
show(cx);
press("tab", cx);
STATE.set(CaptchaState::Wrong);
show(cx);
assert!(!at_root(cx), "Verify stayed a stop through the verdict");
STATE.set(CaptchaState::Passed);
show(cx);
STATE.set(CaptchaState::Asking);
show(cx);
assert!(!at_root(cx), "the mark's focus went back to Verify");
}
/// Enter hands nothing over while the owner checks.
#[gpui::test]
fn enter_waits_while_the_owner_checks(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
cx.simulate_input("W7XK");
press("enter", cx);
STATE.set(CaptchaState::Checking);
show(cx);
cx.simulate_input("again");
press("enter", cx);
assert_eq!(said(), ["W7XK"]);
}
/// Verify with nothing typed sends focus to the field.
#[gpui::test]
fn an_empty_verify_sends_focus_to_the_field(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
press("tab", cx);
press("enter", cx);
assert!(said().is_empty());
cx.simulate_input("W7XK");
press("enter", cx);
assert_eq!(said(), ["W7XK"], "the typing reached the field");
}
/// Enter on Verify hands the answer over and focus to the emptied field.
#[gpui::test]
fn verify_hands_focus_to_the_field(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
cx.simulate_input("W7XK");
press("tab", cx);
press("enter", cx);
assert_eq!(said(), ["W7XK"]);
cx.simulate_input("again");
press("enter", cx);
assert_eq!(said(), ["W7XK", "again"], "the typing reached the field");
}
/// A passed captcha rests: Tab finds no stop in it and typing reaches nothing.
#[gpui::test]
fn a_passed_captcha_rests(cx: &mut TestAppContext) {
STATE.set(CaptchaState::Passed);
let (_, cx) = stage(captcha, cx);
press("tab", cx);
cx.simulate_input("again");
press("enter", cx);
assert!(said().is_empty());
assert!(
!shown("captcha-verified-focused", cx),
"Tab found no stop in the resting captcha"
);
}
/// Verify spins while the owner checks, and its mark keeps its focus once passed.
#[gpui::test]
fn verify_keeps_its_focus_through_the_check(cx: &mut TestAppContext) {
let (_, cx) = stage(captcha, cx);
press("tab", cx);
cx.simulate_input("W7XK");
press("tab", cx);
STATE.set(CaptchaState::Checking);
show(cx);
assert!(!at_root(cx), "a spinning Verify stays a stop");
STATE.set(CaptchaState::Passed);
show(cx);
assert!(!at_root(cx), "the Verified mark took Verify's focus");
}