diff --git a/crates/ely_app/src/shell/web_surface_live_site_tests.rs b/crates/ely_app/src/shell/web_surface_live_site_tests.rs index 0a45e49..81d4d15 100644 --- a/crates/ely_app/src/shell/web_surface_live_site_tests.rs +++ b/crates/ely_app/src/shell/web_surface_live_site_tests.rs @@ -19,7 +19,7 @@ use crate::{ }, shell::{ web_surface_frame::WebSurfaceFrame, - web_surface_geometry::{WebSurfaceScrollOffset, WebSurfaceSize}, + web_surface_geometry::WebSurfaceSize, web_surface_state::{WebSurfaceInputOutcome, WebSurfaceState}, }, }; @@ -33,6 +33,10 @@ const LIVE_SITE_RENDER_ATTEMPTS: usize = 3; const LIVE_SITE_WAIT_TIMEOUT: Duration = Duration::from_secs(20); const LIVE_SITE_WAIT_INTERVAL: Duration = Duration::from_millis(2); const LIVE_SITE_CHILD_ENV: &str = "ELY_APP_WEB_SURFACE_LIVE_CHILD"; +const LIVE_SITE_SCROLL_DOWN_Y: i32 = 360; +const LIVE_SITE_SCROLL_UP_Y: i32 = -240; +const LIVE_SITE_SCROLL_POINT_X: f32 = 320.0; +const LIVE_SITE_SCROLL_POINT_Y: f32 = 320.0; #[test] fn web_surface_cases_cover_prd_reference_urls() -> Result<(), Box> { @@ -53,6 +57,13 @@ fn web_surface_opens_and_renders_prd_reference_sites() -> Result<(), Box Result<(), Box> { + run_isolated_live_site_test("web_surface_scrolls_prd_site_down_and_up", || { + assert_web_surface_scrolls_prd_site() + }) +} + fn run_isolated_live_site_test( test_name: &str, test: impl FnOnce() -> Result<(), Box>, @@ -92,6 +103,69 @@ fn assert_web_surfaces_render(cases: &[LiveSiteCase]) -> Result<(), Box Result<(), Box> { + let mut store = WebSurfaceStore::new(); + let profile_id = ProfileId::new(); + let case = PRD_TOP_SITE_CASES + .iter() + .find(|case| case.url == "https://servo.org/") + .ok_or("missing servo.org live-site case")?; + let tab = web_tab(profile_id, case.url)?; + + assert_eq!( + store.record_viewport_size(tab.id(), live_surface_bounds(), 1.0), + WebSurfaceInputOutcome::Applied, + "{}", + case.url, + ); + store.ensure_surface(&tab, ProfileDataMode::Transient, &[]); + let initial = wait_for_ready_frame_at_scroll(&mut store, tab.id(), case, 0, None)?; + + assert_eq!( + store.record_scroll_delta( + tab.id(), + case.url, + point(px(0.0), px(LIVE_SITE_SCROLL_DOWN_Y as f32)), + live_scroll_point(), + 1.0, + ), + WebSurfaceInputOutcome::Applied, + "{}", + case.url, + ); + store.ensure_surface(&tab, ProfileDataMode::Transient, &[]); + let down = wait_for_ready_frame_at_scroll( + &mut store, + tab.id(), + case, + LIVE_SITE_SCROLL_DOWN_Y, + Some(initial.sample_hash()), + )?; + + assert_eq!( + store.record_scroll_delta( + tab.id(), + case.url, + point(px(0.0), px(LIVE_SITE_SCROLL_UP_Y as f32)), + live_scroll_point(), + 1.0, + ), + WebSurfaceInputOutcome::Applied, + "{}", + case.url, + ); + store.ensure_surface(&tab, ProfileDataMode::Transient, &[]); + wait_for_ready_frame_at_scroll( + &mut store, + tab.id(), + case, + LIVE_SITE_SCROLL_DOWN_Y + LIVE_SITE_SCROLL_UP_Y, + Some(down.sample_hash()), + )?; + store.close_surface(tab.id()); + Ok(()) +} + fn render_web_surface_frame( store: &mut WebSurfaceStore, profile_id: &ProfileId, @@ -140,7 +214,7 @@ fn wait_for_ready_frame( store.tick(std::slice::from_ref(tab_id)); match store.state(tab_id) { Some(WebSurfaceState::Ready(frame)) => { - validate_prd_frame(frame, case)?; + validate_prd_frame(frame, case, 0)?; return Ok(frame.clone()); } Some(WebSurfaceState::Failed { message, .. }) => { @@ -153,7 +227,62 @@ fn wait_for_ready_frame( } } -fn validate_prd_frame(frame: &WebSurfaceFrame, case: &LiveSiteCase) -> Result<(), String> { +fn wait_for_ready_frame_at_scroll( + store: &mut WebSurfaceStore, + tab_id: &TabId, + case: &LiveSiteCase, + expected_scroll_y: i32, + previous_sample_hash: Option, +) -> Result { + let started_at = Instant::now(); + let mut last_error = None; + + loop { + if started_at.elapsed() >= LIVE_SITE_WAIT_TIMEOUT { + return Err(last_error.unwrap_or_else(|| { + format!("timed out rendering {} at scroll y={expected_scroll_y}", case.url) + })); + } + + store.tick(std::slice::from_ref(tab_id)); + match store.state(tab_id) { + Some(WebSurfaceState::Ready(frame)) + if frame.scroll_offset().y() == expected_scroll_y => + { + if let Err(error) = validate_prd_frame(frame, case, expected_scroll_y) { + last_error = Some(error); + thread::sleep(LIVE_SITE_WAIT_INTERVAL); + continue; + } + if !frame.has_hardware_surface() + && let Some(previous_sample_hash) = previous_sample_hash + && frame.sample_hash() == previous_sample_hash + { + last_error = Some(format!( + "{} scroll y={expected_scroll_y} sample hash unchanged", + case.url + )); + thread::sleep(LIVE_SITE_WAIT_INTERVAL); + continue; + } + return Ok(frame.clone()); + } + Some(WebSurfaceState::Ready(_)) => {} + Some(WebSurfaceState::Failed { message, .. }) => { + return Err(format!("{} failed: {message}", case.url)); + } + Some(WebSurfaceState::Loading { .. }) | None => {} + } + + thread::sleep(LIVE_SITE_WAIT_INTERVAL); + } +} + +fn validate_prd_frame( + frame: &WebSurfaceFrame, + case: &LiveSiteCase, + expected_scroll_y: i32, +) -> Result<(), String> { require( frame.size() == WebSurfaceSize { @@ -164,7 +293,7 @@ fn validate_prd_frame(frame: &WebSurfaceFrame, case: &LiveSiteCase) -> Result<() format!("{} size: {:?}", case.url, frame.size()), )?; require( - frame.scroll_offset() == WebSurfaceScrollOffset::default(), + frame.scroll_offset().y() == expected_scroll_y, format!("{} scroll: {:?}", case.url, frame.scroll_offset()), )?; require_render_state_is_open(frame.render_state(), case.url)?; @@ -176,8 +305,13 @@ fn validate_prd_frame(frame: &WebSurfaceFrame, case: &LiveSiteCase) -> Result<() frame.title_label().contains(case.title_fragment), format!("title: {}", frame.title_label()), )?; + let expected_detail = if expected_scroll_y == 0 { + format!("{} 934x657", frame.render_state()) + } else { + format!("{} 934x657 y={expected_scroll_y}", frame.render_state()) + }; require( - frame.detail_label() == format!("{} 934x657", frame.render_state()), + frame.detail_label() == expected_detail, format!("{} detail: {}", case.url, frame.detail_label()), )?; if frame.has_hardware_surface() { @@ -221,6 +355,10 @@ fn live_surface_bounds() -> Bounds { ) } +fn live_scroll_point() -> gpui::Point { + point(px(LIVE_SITE_SCROLL_POINT_X), px(LIVE_SITE_SCROLL_POINT_Y)) +} + fn web_tab(profile_id: ProfileId, url: &str) -> Result> { Ok(BrowserTab::new(TabId::new(), SpaceId::new(), profile_id, "Web", UrlText::parse(url)?)) }