Files
ELY-Browser/crates/ely_app/src/shell/web_surface_scope_tests.rs
T

235 lines
8.3 KiB
Rust

use std::{error::Error, sync::Mutex};
use ely_domain::{BrowserTab, ProfileId, SpaceId, TabId, UrlText};
use gpui::{Bounds, point, px, size};
use crate::services::{
ProfileDataMode,
servo_live::{ServoLiveEnsureRequest, ServoLiveFrame},
};
use super::super::{
web_surface_frame::WebSurfaceFrame,
web_surface_geometry::WebSurfaceScrollOffset,
web_surface_runtime::{
WebSurfaceRuntime, WebSurfaceRuntimeFrame, WebSurfaceUrlChange, WebSurfaceUrlChangeKind,
},
web_surface_state::{WebSurfaceInputOutcome, WebSurfaceState},
web_surface_worker::{LiveRuntimeClient, LiveRuntimeClientError},
};
use super::WebSurfaceStore;
static ENSURES: Mutex<Vec<RecordedEnsure>> = Mutex::new(Vec::new());
#[derive(Debug)]
struct RecordedEnsure {
profile_id: String,
had_input: bool,
}
struct RecordingClient;
impl LiveRuntimeClient for RecordingClient {
fn ensure(
&mut self,
request: ServoLiveEnsureRequest,
) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
ENSURES.lock().map_err(|_| "ensure recorder lock was poisoned".to_string())?.push(
RecordedEnsure {
profile_id: request.profile_id,
had_input: request.scroll_delta_x != 0
|| request.scroll_delta_y != 0
|| request.click_x.is_some()
|| request.click_y.is_some()
|| request.hover_x.is_some()
|| request.hover_y.is_some()
|| request.typed_text.is_some(),
},
);
Ok(None)
}
fn poll(&mut self, _tab_id: String) -> Result<Option<ServoLiveFrame>, LiveRuntimeClientError> {
Ok(None)
}
fn close(&mut self, _tab_id: String) -> Result<(), LiveRuntimeClientError> {
Ok(())
}
}
fn recording_client_factory(
_config_dir: std::path::PathBuf,
) -> Result<Box<dyn LiveRuntimeClient>, String> {
Ok(Box::new(RecordingClient))
}
#[test]
fn profile_scope_change_clears_pixels_input_and_focus_before_ensure() -> Result<(), Box<dyn Error>>
{
ENSURES.lock().map_err(|_| "ensure recorder lock was poisoned")?.clear();
let runtime = WebSurfaceRuntime::new_with_client_factory(recording_client_factory);
let mut store = WebSurfaceStore::new_with_runtime(runtime);
let tab_id = TabId::new();
let url = UrlText::parse("https://example.com/shared")?;
let profile_a = ProfileId::new();
let profile_b = ProfileId::new();
let tab_a =
BrowserTab::new(tab_id.clone(), SpaceId::new(), profile_a.clone(), "Shared", url.clone());
let tab_b = BrowserTab::new(tab_id, SpaceId::new(), profile_b.clone(), "Shared", url);
assert_eq!(
store.record_viewport_size(tab_a.id(), viewport_bounds(), 1.0),
WebSurfaceInputOutcome::Applied,
);
assert!(store.ensure_surface(&tab_a, ProfileDataMode::Transient, &[]));
store.flush_runtime_for_test();
let frame = WebSurfaceFrame::from_live_frame(
tab_a.url().as_str().to_string(),
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test(1, 1, vec![0, 0, 0, 255]),
)?;
store.surface_mut(tab_a.id()).state = Some(WebSurfaceState::Ready(frame));
assert!(
store.state_for_scope(tab_a.id(), &profile_b, ProfileDataMode::Transient).is_none(),
"a model scope change must hide old pixels before the next ensure",
);
assert_eq!(
store
.record_click_point(tab_a.id(), tab_a.url().as_str(), point(px(120.0), px(80.0)), 1.0,),
WebSurfaceInputOutcome::Applied,
);
assert_eq!(
store.record_typed_text(tab_a.id(), tab_a.url().as_str(), "secret"),
WebSurfaceInputOutcome::Applied,
);
assert!(store.ensure_surface(&tab_b, ProfileDataMode::Transient, &[]));
store.flush_runtime_for_test();
assert!(store.keyboard_focus.is_none());
assert!(matches!(
store.state(tab_b.id()),
Some(WebSurfaceState::Loading { previous_frame: None, .. })
));
let ensures = ENSURES.lock().map_err(|_| "ensure recorder lock was poisoned")?;
assert_eq!(ensures.len(), 2);
assert_eq!(ensures[0].profile_id, profile_a.as_str());
assert_eq!(ensures[1].profile_id, profile_b.as_str());
assert!(!ensures[1].had_input);
Ok(())
}
fn viewport_bounds() -> Bounds<gpui::Pixels> {
Bounds::new(point(px(0.0), px(0.0)), size(px(640.0), px(480.0)))
}
#[test]
fn navigation_holds_loading_white_frame_and_accepts_complete_white_page()
-> Result<(), Box<dyn Error>> {
let mut store = WebSurfaceStore::new_with_runtime(WebSurfaceRuntime::new_with_client_factory(
recording_client_factory,
));
let tab_id = TabId::new();
let requested_url = "https://example.com/white".to_string();
let previous = WebSurfaceFrame::from_live_frame(
requested_url.clone(),
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test(1, 1, vec![0, 0, 0, 255]),
)?;
store.surface_mut(&tab_id).state = Some(WebSurfaceState::Loading {
requested_url: requested_url.clone(),
previous_frame: Some(previous),
});
let loading_white = WebSurfaceFrame::from_live_frame(
requested_url.clone(),
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_render_state(1, 1, vec![255; 4], "loading"),
)?;
let complete_white = WebSurfaceFrame::from_live_frame(
requested_url,
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_render_state(1, 1, vec![255; 4], "complete"),
)?;
assert!(store.should_hold_initial_frame(&tab_id, &loading_white, false));
assert!(!store.should_hold_initial_frame(&tab_id, &complete_white, false));
Ok(())
}
#[test]
fn progressive_loading_frame_replaces_an_earlier_ready_frame() -> Result<(), Box<dyn Error>> {
let mut store = WebSurfaceStore::new_with_runtime(WebSurfaceRuntime::new_with_client_factory(
recording_client_factory,
));
let tab_id = TabId::new();
let requested_url = "https://example.com/progressive".to_string();
let first_loading = WebSurfaceFrame::from_live_frame(
requested_url.clone(),
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_render_state(1, 1, vec![16, 32, 64, 255], "loading"),
)?;
store.surface_mut(&tab_id).state = Some(WebSurfaceState::Ready(first_loading));
let next_loading = WebSurfaceFrame::from_live_frame(
requested_url,
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_render_state(1, 1, vec![64, 32, 16, 255], "loading"),
)?;
assert!(!store.should_hold_initial_frame(&tab_id, &next_loading, true));
Ok(())
}
#[test]
fn held_loading_frame_preserves_previous_pixels_and_delivers_metadata() -> Result<(), Box<dyn Error>>
{
let mut store = WebSurfaceStore::new_with_runtime(WebSurfaceRuntime::new_with_client_factory(
recording_client_factory,
));
let tab_id = TabId::new();
let requested_url = "https://example.com/held".to_string();
let previous = WebSurfaceFrame::from_live_frame(
requested_url.clone(),
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_title(1, 1, vec![0, 0, 0, 255], "complete", "Previous"),
)?;
store.surface_mut(&tab_id).state = Some(WebSurfaceState::Loading {
requested_url: requested_url.clone(),
previous_frame: Some(previous),
});
let held = WebSurfaceFrame::from_live_frame(
requested_url,
WebSurfaceScrollOffset::default(),
100,
ServoLiveFrame::for_test_with_title(1, 1, vec![255; 4], "loading", "Updated"),
)?;
let result = store.apply_runtime_frames(vec![WebSurfaceRuntimeFrame::Ready {
tab_id: tab_id.clone(),
frame: Box::new(held),
url_change: Some(WebSurfaceUrlChange {
tab_id: tab_id.clone(),
loaded_url: "https://example.com/redirected".to_string(),
kind: WebSurfaceUrlChangeKind::Observed,
}),
}]);
assert!(matches!(
store.state(&tab_id),
Some(WebSurfaceState::Loading { previous_frame: Some(frame), .. })
if frame.title() == Some("Previous")
));
assert!(matches!(
result.page_metadata.as_slice(),
[metadata] if metadata.title.as_deref() == Some("Updated")
));
assert_eq!(result.url_changes.len(), 1);
Ok(())
}