Survey's Submit asks for any missing answer instead of resting, so no untick takes its focus handle out of the tree. Flashcards turn and go round, and a quiz pick clears its ask, through methods the tests reach; the poll's rows show which one holds the vote. forms::Radio's label wraps in its own block, as Checkbox's does, so a long one stays in a narrow box.
257 lines
7.8 KiB
Rust
257 lines
7.8 KiB
Rust
use std::cell::{Cell, RefCell};
|
|
|
|
use gpui::{AnyElement, IntoElement, Modifiers, TestAppContext, VisualTestContext, point, px};
|
|
|
|
use super::{at_root, press, settle, shown, stage};
|
|
use crate::misc::{Answer, Flashcards, Poll, Question, Quiz, QuizQuestion, Survey};
|
|
|
|
thread_local! {
|
|
static VOTED: Cell<Option<usize>> = const { Cell::new(None) };
|
|
static SAID: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
|
|
}
|
|
|
|
fn said() -> Vec<String> {
|
|
SAID.with(|said| said.borrow().clone())
|
|
}
|
|
|
|
fn say(text: String) {
|
|
SAID.with(|said| said.borrow_mut().push(text));
|
|
}
|
|
|
|
/// Presses just inside the corner of what `selector` marks, where its button starts, as a pointer does.
|
|
fn click(selector: &'static str, cx: &mut VisualTestContext) {
|
|
let corner = cx.debug_bounds(selector).expect("a marked element").origin;
|
|
cx.simulate_click(corner + point(px(4.0), px(4.0)), Modifiers::none());
|
|
settle(cx);
|
|
}
|
|
|
|
fn show(cx: &mut VisualTestContext) {
|
|
cx.update(|window, _| window.refresh());
|
|
settle(cx);
|
|
}
|
|
|
|
fn poll() -> AnyElement {
|
|
let voted = VOTED.get();
|
|
let poll = Poll::new("poll", "Lunch?", ["Soup", "Tea", "Toast"])
|
|
.votes([1, u64::from(voted == Some(1)) + 2, 1])
|
|
.on_vote(|vote, _, _| say(format!("{vote:?}")));
|
|
match voted {
|
|
Some(vote) => poll.voted(vote),
|
|
None => poll,
|
|
}
|
|
.into_any_element()
|
|
}
|
|
|
|
/// Arrows pick, Vote hands it over; the results show each share, and Change vote takes it back on the same stop.
|
|
#[gpui::test]
|
|
fn a_poll_votes_shows_results_and_takes_the_vote_back(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(poll, cx);
|
|
press("tab", cx);
|
|
press("down", cx);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert_eq!(said(), ["Some(1)"]);
|
|
VOTED.set(Some(1));
|
|
show(cx);
|
|
assert!(shown("poll-Tea-60%-voted", cx) && shown("poll-Soup-20%", cx));
|
|
press("enter", cx);
|
|
assert_eq!(said(), ["Some(1)", "None"], "Change vote held Vote's focus");
|
|
VOTED.set(None);
|
|
show(cx);
|
|
press("enter", cx);
|
|
assert_eq!(
|
|
said().last().map(String::as_str),
|
|
Some("Some(1)"),
|
|
"the pick stood"
|
|
);
|
|
assert!(!at_root(cx));
|
|
}
|
|
|
|
/// With no pick, Vote is no stop: Shift-Tab from the radios comes round to them.
|
|
#[gpui::test]
|
|
fn a_poll_rests_until_picked(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(poll, cx);
|
|
press("tab", cx);
|
|
press("shift-tab", cx);
|
|
press("enter", cx);
|
|
assert!(said().is_empty());
|
|
}
|
|
|
|
/// A vote the owner holds seeds the pick, and a pointer's Vote hands focus to the button the results keep.
|
|
#[gpui::test]
|
|
fn a_poll_starts_from_the_owners_vote(cx: &mut TestAppContext) {
|
|
VOTED.set(Some(2));
|
|
let (_, cx) = stage(poll, cx);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
VOTED.set(None);
|
|
show(cx);
|
|
press("shift-tab", cx);
|
|
press("down", cx);
|
|
click("poll-action", cx);
|
|
assert_eq!(said(), ["None", "Some(0)"], "the pick started on Toast");
|
|
VOTED.set(Some(0));
|
|
show(cx);
|
|
assert!(
|
|
!at_root(cx),
|
|
"the radios left, and focus went to Change vote"
|
|
);
|
|
}
|
|
|
|
fn survey() -> AnyElement {
|
|
Survey::new(
|
|
"survey",
|
|
[
|
|
Question::Choice("How often?".into(), vec!["Daily".into(), "Weekly".into()]),
|
|
Question::Several("Which parts?".into(), vec!["Forms".into(), "Charts".into()]),
|
|
Question::Text("Anything else?".into()),
|
|
],
|
|
)
|
|
.on_submit(|answers, _, _| say(format!("{answers:?}")))
|
|
.into_any_element()
|
|
}
|
|
|
|
/// Submit rests until each question is answered, hands the answers over, and the thanks take its focus.
|
|
#[gpui::test]
|
|
fn a_survey_sends_every_answer(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(survey, cx);
|
|
press("tab", cx);
|
|
press("down", cx);
|
|
press("tab", cx);
|
|
press("space", cx);
|
|
press("tab", cx);
|
|
press("tab", cx);
|
|
cx.simulate_input("More charts");
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
let sent = vec![
|
|
Answer::Choice(1),
|
|
Answer::Several(vec![0]),
|
|
Answer::Text("More charts".into()),
|
|
];
|
|
assert_eq!(said(), [format!("{sent:?}")]);
|
|
assert!(shown("survey-sent", cx) && !at_root(cx));
|
|
}
|
|
|
|
/// Submit asks for what is missing: words of only spaces, or a box ticked and unticked; a pointer's Submit hands focus to the thanks.
|
|
#[gpui::test]
|
|
fn a_survey_waits_for_every_answer(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(survey, cx);
|
|
press("tab", cx);
|
|
press("down", cx);
|
|
press("tab", cx);
|
|
press("space", cx);
|
|
press("tab", cx);
|
|
press("tab", cx);
|
|
cx.simulate_input(" ");
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert!(
|
|
said().is_empty() && shown("survey-answer-first", cx),
|
|
"blank words are no answer"
|
|
);
|
|
press("shift-tab", cx);
|
|
cx.simulate_input("Fine");
|
|
press("shift-tab", cx);
|
|
press("shift-tab", cx);
|
|
press("space", cx);
|
|
for _ in 0..3 {
|
|
press("tab", cx);
|
|
}
|
|
press("enter", cx);
|
|
assert!(said().is_empty(), "no box ticked is no answer");
|
|
assert!(!at_root(cx), "Submit kept its focus");
|
|
press("shift-tab", cx);
|
|
press("shift-tab", cx);
|
|
press("shift-tab", cx);
|
|
press("space", cx);
|
|
click("survey-action", cx);
|
|
assert_eq!(said().len(), 1);
|
|
assert!(!at_root(cx), "the field left, and focus went to the thanks");
|
|
}
|
|
|
|
fn quiz() -> AnyElement {
|
|
Quiz::new(
|
|
"quiz",
|
|
[
|
|
QuizQuestion::new("2 + 2?", ["3", "4"], 1),
|
|
QuizQuestion::new("Blue and yellow?", ["Green", "Red"], 0),
|
|
],
|
|
)
|
|
.on_score(|right, count, _, _| say(format!("{right} of {count}")))
|
|
.into_any_element()
|
|
}
|
|
|
|
/// One button checks, goes on, scores and starts again, and asks for a pick when none is made.
|
|
#[gpui::test]
|
|
fn a_quiz_checks_each_answer_and_scores(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(quiz, cx);
|
|
press("tab", cx);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert!(shown("quiz-pick-first", cx));
|
|
press("shift-tab", cx);
|
|
press("down", cx);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert!(shown("quiz-row-4-true", cx), "the right answer shows");
|
|
press("enter", cx);
|
|
press("shift-tab", cx);
|
|
press("down", cx);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert!(shown("quiz-row-Green-true", cx) && shown("quiz-row-Red-false", cx));
|
|
press("enter", cx);
|
|
assert!(shown("quiz-score-1-2", cx));
|
|
assert_eq!(said(), ["1 of 2"]);
|
|
press("enter", cx);
|
|
assert!(!at_root(cx), "Try again kept the button's focus");
|
|
}
|
|
|
|
/// A pointer's Check hands focus from the radios, which leave, to the one button.
|
|
#[gpui::test]
|
|
fn a_pointers_check_keeps_focus_in_the_quiz(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(quiz, cx);
|
|
press("tab", cx);
|
|
press("down", cx);
|
|
click("quiz-action", cx);
|
|
assert!(shown("quiz-row-4-true", cx) && !at_root(cx));
|
|
}
|
|
|
|
fn cards() -> AnyElement {
|
|
Flashcards::new("cards", [("A", "a"), ("B", "b"), ("C", "c")]).into_any_element()
|
|
}
|
|
|
|
/// A press turns the card; Next and Previous go round the deck, and each card they reach shows its front.
|
|
#[gpui::test]
|
|
fn flashcards_turn_and_go_round(cx: &mut TestAppContext) {
|
|
let (_, cx) = stage(cards, cx);
|
|
assert!(shown("flashcard-front-A", cx));
|
|
for _ in 0..3 {
|
|
press("tab", cx);
|
|
}
|
|
press("enter", cx);
|
|
press("enter", cx);
|
|
assert!(shown("flashcard-front-C", cx));
|
|
press("enter", cx);
|
|
press("shift-tab", cx);
|
|
press("shift-tab", cx);
|
|
press("enter", cx);
|
|
assert!(
|
|
shown("flashcard-back-a", cx) && !shown("flashcard-back-c", cx),
|
|
"Next went round from the last card to the first, and the press turned it"
|
|
);
|
|
press("tab", cx);
|
|
press("enter", cx);
|
|
assert!(
|
|
!shown("flashcard-back-c", cx),
|
|
"the card Previous reached shows its front"
|
|
);
|
|
press("shift-tab", cx);
|
|
press("enter", cx);
|
|
assert!(
|
|
shown("flashcard-back-c", cx),
|
|
"Previous went round from the first card to the last"
|
|
);
|
|
}
|