167 lines
5.1 KiB
Rust
167 lines
5.1 KiB
Rust
use ai_app::ui::fixture::{PHONE_FRAME_MS, PHONE_SCALE, phone_size};
|
|
use iris::harness::{Harness, TouchAction, TouchScript};
|
|
use iris::prelude::*;
|
|
use iris::sense::DRAG_SLOP;
|
|
|
|
fn opened() -> (Harness, ai_app::ui::TranscriptScreen) {
|
|
let mut h = Harness::new(phone_size(), PHONE_SCALE);
|
|
let opened = ai_app::ui::fixture::open(&mut h.rsc, &mut h.state).expect("the fixture folds");
|
|
h.frame(0);
|
|
h.frame(PHONE_FRAME_MS);
|
|
(h, opened.screen)
|
|
}
|
|
|
|
fn tracked_row(h: &mut Harness, screen: &ai_app::ui::TranscriptScreen) -> (RowKey, f32) {
|
|
let middle = phone_size().y / 2.0;
|
|
let list = (screen.list)(&mut h.rsc);
|
|
let key = list.key_at(middle).expect("a row under the viewport");
|
|
let (top, _) = list.extent(key).expect("that row has an extent");
|
|
(key, top)
|
|
}
|
|
|
|
fn row_top(h: &mut Harness, screen: &ai_app::ui::TranscriptScreen, key: RowKey) -> f32 {
|
|
(screen.list)(&mut h.rsc)
|
|
.extent(key)
|
|
.expect("the tracked row is still loaded")
|
|
.0
|
|
}
|
|
|
|
const STEP: f32 = 2.0;
|
|
const SAMPLES: usize = 3;
|
|
const CATCH_X: f32 = 540.0;
|
|
|
|
fn drag_from(
|
|
h: &mut Harness,
|
|
screen: &ai_app::ui::TranscriptScreen,
|
|
key: RowKey,
|
|
y0: f32,
|
|
t0: u64,
|
|
expect_tracking: bool,
|
|
) -> u64 {
|
|
let before = row_top(h, screen, key);
|
|
h.touch(TouchAction::Down, Vec2::new(CATCH_X, y0), t0);
|
|
assert_eq!(
|
|
row_top(h, screen, key),
|
|
before,
|
|
"the down itself must not move the content, only stop it"
|
|
);
|
|
let mut t = t0;
|
|
for i in 1..=SAMPLES {
|
|
let moved = STEP * i as f32;
|
|
t = t0 + 8 * i as u64;
|
|
h.touch(TouchAction::Move, Vec2::new(CATCH_X, y0 + moved), t);
|
|
let travelled = row_top(h, screen, key) - before;
|
|
if expect_tracking {
|
|
assert!(
|
|
(travelled - moved).abs() < 0.5,
|
|
"sample {i}: the finger has moved {moved}px since the down and the content \
|
|
{travelled:.1}px -- it is not pinned to the finger"
|
|
);
|
|
} else {
|
|
assert!(
|
|
travelled.abs() < 0.5,
|
|
"sample {i}: a {moved}px drag is inside DRAG_SLOP ({DRAG_SLOP}px) and must move \
|
|
nothing, but the content moved {travelled:.1}px"
|
|
);
|
|
}
|
|
}
|
|
t += 8;
|
|
h.touch(
|
|
TouchAction::Up,
|
|
Vec2::new(CATCH_X, y0 + STEP * SAMPLES as f32),
|
|
t,
|
|
);
|
|
t
|
|
}
|
|
|
|
#[test]
|
|
fn a_press_on_a_flinging_list_pins_the_content_to_the_finger() {
|
|
let (mut h, screen) = opened();
|
|
|
|
let flick = TouchScript::parse(include_str!("../touch/flick-120hz.touch"))
|
|
.unwrap_or_else(|e| panic!("flick-120hz.touch: {e}"));
|
|
h.replay(&flick);
|
|
|
|
let catch_at = flick.end_ms() + 150;
|
|
h.frames_until(
|
|
flick.end_ms() + PHONE_FRAME_MS,
|
|
catch_at - PHONE_FRAME_MS,
|
|
PHONE_FRAME_MS,
|
|
);
|
|
assert!(
|
|
(screen.list)(&mut h.rsc).is_scrolling(),
|
|
"the fling must still be running 150ms in, or this test catches nothing"
|
|
);
|
|
|
|
let (key, _) = tracked_row(&mut h, &screen);
|
|
drag_from(&mut h, &screen, key, 1200.0, catch_at, true);
|
|
}
|
|
|
|
#[test]
|
|
fn a_catch_that_never_moved_is_not_a_tap_and_does_not_fling() {
|
|
let (mut h, screen) = opened();
|
|
|
|
let flick = TouchScript::parse(include_str!("../touch/flick-120hz.touch"))
|
|
.unwrap_or_else(|e| panic!("flick-120hz.touch: {e}"));
|
|
h.replay(&flick);
|
|
let catch_at = flick.end_ms() + 150;
|
|
h.frames_until(
|
|
flick.end_ms() + PHONE_FRAME_MS,
|
|
catch_at - PHONE_FRAME_MS,
|
|
PHONE_FRAME_MS,
|
|
);
|
|
assert!(
|
|
(screen.list)(&mut h.rsc).is_scrolling(),
|
|
"the fling must still be running 150ms in, or this test catches nothing"
|
|
);
|
|
|
|
let (key, _) = tracked_row(&mut h, &screen);
|
|
h.touch(TouchAction::Down, Vec2::new(CATCH_X, 1200.0), catch_at);
|
|
let stopped_at = row_top(&mut h, &screen, key);
|
|
h.touch(TouchAction::Up, Vec2::new(CATCH_X, 1200.0), catch_at + 8);
|
|
|
|
assert_eq!(
|
|
(screen.list)(&mut h.rsc).fling_velocity(),
|
|
None,
|
|
"a press that stopped a fling and moved nothing must not start another"
|
|
);
|
|
h.frames_until(catch_at + 16, catch_at + 500, PHONE_FRAME_MS);
|
|
assert!(
|
|
(row_top(&mut h, &screen, key) - stopped_at).abs() < 0.5,
|
|
"the content moved after a catch was released without moving"
|
|
);
|
|
assert_eq!(
|
|
h.state.opened_urls,
|
|
Vec::<String>::new(),
|
|
"a catch is not a tap: nothing under it may be followed"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_same_small_drag_on_a_settled_list_moves_nothing() {
|
|
let (mut h, screen) = opened();
|
|
|
|
let flick = TouchScript::parse(include_str!("../touch/flick-120hz.touch"))
|
|
.unwrap_or_else(|e| panic!("flick-120hz.touch: {e}"));
|
|
h.replay(&flick);
|
|
let settled = h.frames_until(
|
|
flick.end_ms() + PHONE_FRAME_MS,
|
|
flick.end_ms() + 4000,
|
|
PHONE_FRAME_MS,
|
|
);
|
|
assert!(
|
|
!(screen.list)(&mut h.rsc).is_scrolling(),
|
|
"the fling must have stopped, or this is the same case as the test above"
|
|
);
|
|
|
|
let (key, _) = tracked_row(&mut h, &screen);
|
|
drag_from(
|
|
&mut h,
|
|
&screen,
|
|
key,
|
|
1200.0,
|
|
settled + PHONE_FRAME_MS,
|
|
false,
|
|
);
|
|
}
|