browser_tester 1.5.0

Deterministic lightweight browser runtime for Rust tests
Documentation
use super::*;

#[derive(Debug)]
pub(crate) struct DomRuntimeState {
    pub(crate) window_object: Rc<RefCell<ObjectValue>>,
    pub(crate) document_object: Rc<RefCell<ObjectValue>>,
    pub(crate) location_object: Rc<RefCell<ObjectValue>>,
    pub(crate) selection_object: Rc<RefCell<ObjectValue>>,
    pub(crate) document_ready_state: String,
    pub(crate) document_visibility_state: String,
    pub(crate) document_scroll_x: i64,
    pub(crate) document_scroll_y: i64,
    pub(crate) node_event_handler_props: HashMap<(NodeId, String), ScriptHandler>,
    pub(crate) node_expando_props: HashMap<(NodeId, String), Value>,
    pub(crate) live_dom_string_maps: HashMap<NodeId, Rc<RefCell<ObjectValue>>>,
    pub(crate) live_class_lists: HashMap<NodeId, Rc<RefCell<ObjectValue>>>,
    pub(crate) live_child_nodes_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_children_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_form_elements_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_form_named_group_lists: HashMap<(NodeId, String), Rc<RefCell<NodeListValue>>>,
    pub(crate) live_select_options_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_selected_options_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_datalist_options_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_media_text_tracks_lists: HashMap<NodeId, Rc<RefCell<NodeListValue>>>,
    pub(crate) live_text_track_objects: HashMap<NodeId, Rc<RefCell<ObjectValue>>>,
    pub(crate) live_media_time_ranges_objects: HashMap<(NodeId, String), Rc<RefCell<ObjectValue>>>,
    pub(crate) live_named_node_maps: HashMap<NodeId, Rc<RefCell<ObjectValue>>>,
    pub(crate) live_document_forms_list: Option<Rc<RefCell<NodeListValue>>>,
    pub(crate) live_document_images_list: Option<Rc<RefCell<NodeListValue>>>,
    pub(crate) live_document_links_list: Option<Rc<RefCell<NodeListValue>>>,
    pub(crate) live_document_scripts_list: Option<Rc<RefCell<NodeListValue>>>,
    pub(crate) node_animations: Vec<NodeAnimationRecord>,
    pub(crate) pointer_capture_targets: HashMap<i64, NodeId>,
    pub(crate) shadow_roots: HashMap<NodeId, ShadowRootRecord>,
    pub(crate) dialog_return_values: HashMap<NodeId, String>,
    pub(crate) click_in_progress: HashSet<NodeId>,
    pub(crate) focused_form_control_values: HashMap<NodeId, String>,
    pub(crate) pending_form_control_change: HashSet<NodeId>,
}

impl Default for DomRuntimeState {
    fn default() -> Self {
        Self {
            window_object: Rc::new(RefCell::new(ObjectValue::default())),
            document_object: Rc::new(RefCell::new(ObjectValue::default())),
            location_object: Rc::new(RefCell::new(ObjectValue::default())),
            selection_object: Rc::new(RefCell::new(ObjectValue::default())),
            document_ready_state: "complete".to_string(),
            document_visibility_state: "visible".to_string(),
            document_scroll_x: 0,
            document_scroll_y: 0,
            node_event_handler_props: HashMap::new(),
            node_expando_props: HashMap::new(),
            live_dom_string_maps: HashMap::new(),
            live_class_lists: HashMap::new(),
            live_child_nodes_lists: HashMap::new(),
            live_children_lists: HashMap::new(),
            live_form_elements_lists: HashMap::new(),
            live_form_named_group_lists: HashMap::new(),
            live_select_options_lists: HashMap::new(),
            live_selected_options_lists: HashMap::new(),
            live_datalist_options_lists: HashMap::new(),
            live_media_text_tracks_lists: HashMap::new(),
            live_text_track_objects: HashMap::new(),
            live_media_time_ranges_objects: HashMap::new(),
            live_named_node_maps: HashMap::new(),
            live_document_forms_list: None,
            live_document_images_list: None,
            live_document_links_list: None,
            live_document_scripts_list: None,
            node_animations: Vec::new(),
            pointer_capture_targets: HashMap::new(),
            shadow_roots: HashMap::new(),
            dialog_return_values: HashMap::new(),
            click_in_progress: HashSet::new(),
            focused_form_control_values: HashMap::new(),
            pending_form_control_change: HashSet::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct NodeAnimationRecord {
    pub(crate) target: NodeId,
    pub(crate) animation: Rc<RefCell<ObjectValue>>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ShadowRootMode {
    Open,
    Closed,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ShadowRootRecord {
    pub(crate) root: NodeId,
    pub(crate) mode: ShadowRootMode,
    pub(crate) serializable: bool,
}

#[derive(Debug, Default)]
pub(crate) struct PlatformMockState {
    pub(crate) clipboard_text: String,
    pub(crate) clipboard_read_error: Option<String>,
    pub(crate) clipboard_write_error: Option<String>,
    pub(crate) fetch_mocks: HashMap<String, FetchMockResponse>,
    pub(crate) fetch_calls: Vec<String>,
    pub(crate) match_media_mocks: HashMap<String, bool>,
    pub(crate) match_media_calls: Vec<String>,
    pub(crate) default_match_media_matches: bool,
    pub(crate) alert_messages: Vec<String>,
    pub(crate) print_call_count: usize,
    pub(crate) confirm_responses: VecDeque<bool>,
    pub(crate) default_confirm_response: bool,
    pub(crate) prompt_responses: VecDeque<Option<String>>,
    pub(crate) default_prompt_response: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FetchMockResponse {
    pub(crate) status: i64,
    pub(crate) status_text: String,
    pub(crate) body: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CookieRecord {
    pub(crate) name: String,
    pub(crate) value: String,
    pub(crate) domain: Option<String>,
    pub(crate) path: String,
    pub(crate) expires_ms: Option<i64>,
    pub(crate) secure: bool,
    pub(crate) same_site: Option<String>,
    pub(crate) partitioned: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CacheEntryRecord {
    pub(crate) request_url: String,
    pub(crate) response_url: String,
    pub(crate) response_status: i64,
    pub(crate) response_status_text: String,
    pub(crate) response_body: String,
}

#[derive(Debug)]
pub(crate) struct TraceState {
    pub(crate) enabled: bool,
    pub(crate) events: bool,
    pub(crate) timers: bool,
    pub(crate) logs: VecDeque<String>,
    pub(crate) log_limit: usize,
    pub(crate) to_stderr: bool,
}

impl Default for TraceState {
    fn default() -> Self {
        Self {
            enabled: false,
            events: true,
            timers: true,
            logs: VecDeque::new(),
            log_limit: 10_000,
            to_stderr: true,
        }
    }
}

#[derive(Debug)]
pub(crate) struct BrowserApiState {
    pub(crate) next_url_object_id: usize,
    pub(crate) url_objects: HashMap<usize, Rc<RefCell<ObjectValue>>>,
    pub(crate) url_constructor_properties: Rc<RefCell<ObjectValue>>,
    pub(crate) local_storage_object: Rc<RefCell<ObjectValue>>,
    pub(crate) cookie_store_object: Rc<RefCell<ObjectValue>>,
    pub(crate) cache_storage_object: Rc<RefCell<ObjectValue>>,
    pub(crate) caches_by_name: HashMap<String, Rc<RefCell<ObjectValue>>>,
    pub(crate) cache_names_in_order: Vec<String>,
    pub(crate) cache_entries_by_name: HashMap<String, Vec<CacheEntryRecord>>,
    pub(crate) window_closed: bool,
    pub(crate) window_screen_x: i64,
    pub(crate) window_screen_y: i64,
    pub(crate) cookies: Vec<CookieRecord>,
    pub(crate) cookie_store_change_listeners: Vec<Value>,
    pub(crate) next_blob_url_id: usize,
    pub(crate) blob_url_objects: HashMap<String, Rc<RefCell<BlobValue>>>,
    pub(crate) downloads: Vec<DownloadArtifact>,
    pub(crate) clipboard_writes: Vec<ClipboardWriteArtifact>,
}

impl Default for BrowserApiState {
    fn default() -> Self {
        Self {
            next_url_object_id: 1,
            url_objects: HashMap::new(),
            url_constructor_properties: Rc::new(RefCell::new(ObjectValue::default())),
            local_storage_object: Rc::new(RefCell::new(ObjectValue::default())),
            cookie_store_object: Rc::new(RefCell::new(ObjectValue::default())),
            cache_storage_object: Rc::new(RefCell::new(ObjectValue::default())),
            caches_by_name: HashMap::new(),
            cache_names_in_order: Vec::new(),
            cache_entries_by_name: HashMap::new(),
            window_closed: false,
            window_screen_x: 0,
            window_screen_y: 0,
            cookies: Vec::new(),
            cookie_store_change_listeners: Vec::new(),
            next_blob_url_id: 1,
            blob_url_objects: HashMap::new(),
            downloads: Vec::new(),
            clipboard_writes: Vec::new(),
        }
    }
}

impl BrowserApiState {
    pub(crate) fn allocate_url_object_id(&mut self) -> usize {
        let id = self.next_url_object_id;
        self.next_url_object_id = self.next_url_object_id.saturating_add(1);
        id
    }

    pub(crate) fn allocate_blob_url(&mut self) -> String {
        let object_url = format!("blob:bt-{}", self.next_blob_url_id);
        self.next_blob_url_id = self.next_blob_url_id.saturating_add(1);
        object_url
    }
}

#[derive(Debug)]
pub(crate) struct PromiseRuntimeState {
    pub(crate) next_promise_id: usize,
}

impl Default for PromiseRuntimeState {
    fn default() -> Self {
        Self { next_promise_id: 1 }
    }
}

impl PromiseRuntimeState {
    pub(crate) fn allocate_promise_id(&mut self) -> usize {
        let id = self.next_promise_id;
        self.next_promise_id = self.next_promise_id.saturating_add(1);
        id
    }
}

#[derive(Debug)]
pub(crate) struct SymbolRuntimeState {
    pub(crate) next_symbol_id: usize,
    pub(crate) symbol_registry: HashMap<String, Rc<SymbolValue>>,
    pub(crate) symbols_by_id: HashMap<usize, Rc<SymbolValue>>,
    pub(crate) well_known_symbols: HashMap<String, Rc<SymbolValue>>,
}

impl Default for SymbolRuntimeState {
    fn default() -> Self {
        Self {
            next_symbol_id: 1,
            symbol_registry: HashMap::new(),
            symbols_by_id: HashMap::new(),
            well_known_symbols: HashMap::new(),
        }
    }
}

impl SymbolRuntimeState {
    pub(crate) fn allocate_symbol_id(&mut self) -> usize {
        let id = self.next_symbol_id;
        self.next_symbol_id = self.next_symbol_id.saturating_add(1);
        id
    }
}

#[derive(Debug)]
pub(crate) struct SchedulerState {
    pub(crate) task_queue: Vec<ScheduledTask>,
    pub(crate) microtask_queue: VecDeque<ScheduledMicrotask>,
    pub(crate) now_ms: i64,
    pub(crate) timer_step_limit: usize,
    pub(crate) next_timer_id: i64,
    pub(crate) next_task_order: i64,
    pub(crate) task_depth: usize,
    pub(crate) running_timer_id: Option<i64>,
    pub(crate) running_timer_canceled: bool,
}

impl Default for SchedulerState {
    fn default() -> Self {
        Self {
            task_queue: Vec::new(),
            microtask_queue: VecDeque::new(),
            now_ms: 0,
            timer_step_limit: 10_000,
            next_timer_id: 1,
            next_task_order: 0,
            task_depth: 0,
            running_timer_id: None,
            running_timer_canceled: false,
        }
    }
}

impl SchedulerState {
    pub(crate) fn allocate_timer_id(&mut self) -> i64 {
        let id = self.next_timer_id;
        self.next_timer_id += 1;
        id
    }

    pub(crate) fn allocate_task_order(&mut self) -> i64 {
        let order = self.next_task_order;
        self.next_task_order += 1;
        order
    }
}