viewpoint_core/page/events/page_handlers/
mod.rs

1//! Page event handler methods.
2//!
3//! This module contains the `impl Page` block with methods for setting up
4//! event handlers (on_dialog, on_console, etc.) and waiting for events.
5
6use std::time::Duration;
7
8use super::super::console::ConsoleMessage;
9use super::super::dialog::Dialog;
10use super::super::download::Download;
11use super::super::file_chooser::FileChooser;
12use super::super::frame::Frame;
13use super::super::page_error::PageError as PageErrorInfo;
14use super::super::Page;
15use crate::error::{LocatorError, PageError};
16
17/// Default timeout for navigation and event waiting.
18const DEFAULT_NAVIGATION_TIMEOUT: Duration = Duration::from_secs(30);
19
20impl Page {
21    // =========================================================================
22    // Dialog Handling Methods
23    // =========================================================================
24
25    /// Set a handler for browser dialogs (alert, confirm, prompt, beforeunload).
26    ///
27    /// The handler will be called whenever a dialog appears. If no handler is
28    /// set, dialogs are automatically dismissed.
29    ///
30    /// # Example
31    ///
32    /// ```ignore
33    /// // Accept all dialogs
34    /// page.on_dialog(|dialog| async move {
35    ///     println!("Dialog: {} - {}", dialog.type_(), dialog.message());
36    ///     dialog.accept().await
37    /// }).await;
38    ///
39    /// // Handle prompt with custom text
40    /// page.on_dialog(|dialog| async move {
41    ///     if matches!(dialog.type_(), DialogType::Prompt) {
42    ///         dialog.accept_with_text("my answer").await
43    ///     } else {
44    ///         dialog.accept().await
45    ///     }
46    /// }).await;
47    /// ```
48    pub async fn on_dialog<F, Fut>(&self, handler: F)
49    where
50        F: Fn(Dialog) -> Fut + Send + Sync + 'static,
51        Fut: std::future::Future<Output = Result<(), PageError>> + Send + 'static,
52    {
53        self.event_manager.set_dialog_handler(handler).await;
54    }
55
56    /// Remove the dialog handler.
57    ///
58    /// After calling this, dialogs will be automatically dismissed.
59    pub async fn off_dialog(&self) {
60        self.event_manager.remove_dialog_handler().await;
61    }
62
63    // =========================================================================
64    // Console Event Methods
65    // =========================================================================
66
67    /// Set a handler for console messages (console.log, console.error, etc.).
68    ///
69    /// The handler will be called whenever JavaScript code logs to the console.
70    ///
71    /// # Example
72    ///
73    /// ```ignore
74    /// page.on_console(|message| async move {
75    ///     println!("[{}] {}", message.type_(), message.text());
76    /// }).await;
77    ///
78    /// // Filter by message type
79    /// page.on_console(|message| async move {
80    ///     if matches!(message.type_(), ConsoleMessageType::Error) {
81    ///         eprintln!("Console error: {}", message.text());
82    ///     }
83    /// }).await;
84    /// ```
85    pub async fn on_console<F, Fut>(&self, handler: F)
86    where
87        F: Fn(ConsoleMessage) -> Fut + Send + Sync + 'static,
88        Fut: std::future::Future<Output = ()> + Send + 'static,
89    {
90        self.event_manager.set_console_handler(handler).await;
91    }
92
93    /// Remove the console message handler.
94    pub async fn off_console(&self) {
95        self.event_manager.remove_console_handler().await;
96    }
97
98    // =========================================================================
99    // Page Error Event Methods
100    // =========================================================================
101
102    /// Set a handler for page errors (uncaught exceptions).
103    ///
104    /// The handler will be called whenever an uncaught JavaScript exception occurs.
105    ///
106    /// # Example
107    ///
108    /// ```ignore
109    /// page.on_pageerror(|error| async move {
110    ///     eprintln!("Page error: {}", error.message());
111    ///     if let Some(stack) = error.stack() {
112    ///         eprintln!("Stack trace:\n{}", stack);
113    ///     }
114    /// }).await;
115    /// ```
116    pub async fn on_pageerror<F, Fut>(&self, handler: F)
117    where
118        F: Fn(PageErrorInfo) -> Fut + Send + Sync + 'static,
119        Fut: std::future::Future<Output = ()> + Send + 'static,
120    {
121        self.event_manager.set_pageerror_handler(handler).await;
122    }
123
124    /// Remove the page error handler.
125    pub async fn off_pageerror(&self) {
126        self.event_manager.remove_pageerror_handler().await;
127    }
128
129    // =========================================================================
130    // Frame Event Methods
131    // =========================================================================
132
133    /// Set a handler for frame attached events.
134    ///
135    /// The handler will be called whenever a new frame is attached to the page,
136    /// typically when an `<iframe>` is added to the DOM.
137    ///
138    /// # Example
139    ///
140    /// ```ignore
141    /// page.on_frameattached(|frame| async move {
142    ///     println!("Frame attached: {}", frame.url());
143    /// }).await;
144    /// ```
145    pub async fn on_frameattached<F, Fut>(&self, handler: F)
146    where
147        F: Fn(Frame) -> Fut + Send + Sync + 'static,
148        Fut: std::future::Future<Output = ()> + Send + 'static,
149    {
150        self.event_manager.set_frameattached_handler(handler).await;
151    }
152
153    /// Remove the frame attached handler.
154    pub async fn off_frameattached(&self) {
155        self.event_manager.remove_frameattached_handler().await;
156    }
157
158    /// Set a handler for frame navigated events.
159    ///
160    /// The handler will be called whenever a frame navigates to a new URL.
161    ///
162    /// # Example
163    ///
164    /// ```ignore
165    /// page.on_framenavigated(|frame| async move {
166    ///     println!("Frame navigated to: {}", frame.url());
167    /// }).await;
168    /// ```
169    pub async fn on_framenavigated<F, Fut>(&self, handler: F)
170    where
171        F: Fn(Frame) -> Fut + Send + Sync + 'static,
172        Fut: std::future::Future<Output = ()> + Send + 'static,
173    {
174        self.event_manager.set_framenavigated_handler(handler).await;
175    }
176
177    /// Remove the frame navigated handler.
178    pub async fn off_framenavigated(&self) {
179        self.event_manager.remove_framenavigated_handler().await;
180    }
181
182    /// Set a handler for frame detached events.
183    ///
184    /// The handler will be called whenever a frame is detached from the page,
185    /// typically when an `<iframe>` is removed from the DOM.
186    ///
187    /// # Example
188    ///
189    /// ```ignore
190    /// page.on_framedetached(|frame| async move {
191    ///     println!("Frame detached: {}", frame.id());
192    /// }).await;
193    /// ```
194    pub async fn on_framedetached<F, Fut>(&self, handler: F)
195    where
196        F: Fn(Frame) -> Fut + Send + Sync + 'static,
197        Fut: std::future::Future<Output = ()> + Send + 'static,
198    {
199        self.event_manager.set_framedetached_handler(handler).await;
200    }
201
202    /// Remove the frame detached handler.
203    pub async fn off_framedetached(&self) {
204        self.event_manager.remove_framedetached_handler().await;
205    }
206
207    // =========================================================================
208    // Expect Methods (Wait for events triggered by actions)
209    // =========================================================================
210
211    /// Wait for a console message triggered by an action.
212    ///
213    /// # Example
214    ///
215    /// ```ignore
216    /// let message = page.expect_console(async {
217    ///     page.locator("#log-button").click().await?;
218    ///     Ok(())
219    /// }).await?;
220    ///
221    /// println!("Console message: {}", message.text());
222    /// ```
223    pub async fn expect_console<F, Fut>(&self, action: F) -> Result<ConsoleMessage, PageError>
224    where
225        F: FnOnce() -> Fut,
226        Fut: std::future::Future<Output = Result<(), LocatorError>>,
227    {
228        let timeout = DEFAULT_NAVIGATION_TIMEOUT;
229        let console_future = self.event_manager.wait_for_console(timeout);
230        
231        // Perform the action
232        action().await.map_err(|e| PageError::EvaluationFailed(e.to_string()))?;
233        
234        // Wait for the console message
235        console_future.await
236    }
237
238    /// Wait for a page error triggered by an action.
239    ///
240    /// # Example
241    ///
242    /// ```ignore
243    /// let error = page.expect_pageerror(async {
244    ///     page.locator("#trigger-error").click().await?;
245    ///     Ok(())
246    /// }).await?;
247    ///
248    /// println!("Page error: {}", error.message());
249    /// ```
250    pub async fn expect_pageerror<F, Fut>(&self, action: F) -> Result<PageErrorInfo, PageError>
251    where
252        F: FnOnce() -> Fut,
253        Fut: std::future::Future<Output = Result<(), LocatorError>>,
254    {
255        let timeout = DEFAULT_NAVIGATION_TIMEOUT;
256        let pageerror_future = self.event_manager.wait_for_pageerror(timeout);
257        
258        // Perform the action
259        action().await.map_err(|e| PageError::EvaluationFailed(e.to_string()))?;
260        
261        // Wait for the page error
262        pageerror_future.await
263    }
264
265    // =========================================================================
266    // Download Handling Methods
267    // =========================================================================
268
269    /// Set a handler for file downloads.
270    ///
271    /// The handler will be called whenever a download starts.
272    ///
273    /// # Example
274    ///
275    /// ```ignore
276    /// page.on_download(|download| async move {
277    ///     let path = download.path().await.unwrap();
278    ///     println!("Downloaded: {}", path.display());
279    /// }).await;
280    /// ```
281    pub async fn on_download<F, Fut>(&self, handler: F)
282    where
283        F: Fn(Download) -> Fut + Send + Sync + 'static,
284        Fut: std::future::Future<Output = ()> + Send + 'static,
285    {
286        // Enable downloads first
287        let _ = self.event_manager.set_download_behavior(true).await;
288        self.event_manager.set_download_handler(handler).await;
289    }
290
291    /// Wait for a download triggered by an action.
292    ///
293    /// # Example
294    ///
295    /// ```ignore
296    /// let download = page.wait_for_download(async {
297    ///     page.locator("a.download").click().await?;
298    ///     Ok(())
299    /// }).await?;
300    ///
301    /// download.save_as("./my-file.pdf").await?;
302    /// ```
303    pub async fn expect_download<F, Fut>(&self, action: F) -> Result<Download, PageError>
304    where
305        F: FnOnce() -> Fut,
306        Fut: std::future::Future<Output = Result<(), LocatorError>>,
307    {
308        // Enable downloads first
309        self.event_manager.set_download_behavior(true).await?;
310
311        // Start waiting and then perform action
312        let timeout = DEFAULT_NAVIGATION_TIMEOUT;
313        let download_future = self.event_manager.wait_for_download(timeout);
314        
315        // Perform the action
316        action().await.map_err(|e| PageError::EvaluationFailed(e.to_string()))?;
317        
318        // Wait for the download
319        download_future.await
320    }
321
322    // =========================================================================
323    // File Chooser Handling Methods
324    // =========================================================================
325
326    /// Set whether to intercept file chooser dialogs.
327    ///
328    /// When enabled, file chooser dialogs will be intercepted and the
329    /// `on_filechooser` handler will be called instead of showing the
330    /// native file picker.
331    pub async fn set_intercept_file_chooser(&self, enabled: bool) -> Result<(), PageError> {
332        self.event_manager.set_intercept_file_chooser(enabled).await
333    }
334
335    /// Set a handler for file chooser dialogs.
336    ///
337    /// You must call `set_intercept_file_chooser(true)` before using this.
338    ///
339    /// # Example
340    ///
341    /// ```ignore
342    /// page.set_intercept_file_chooser(true).await?;
343    /// page.on_filechooser(|chooser| async move {
344    ///     chooser.set_files(&["./upload.txt"]).await.unwrap();
345    /// }).await;
346    /// ```
347    pub async fn on_filechooser<F, Fut>(&self, handler: F)
348    where
349        F: Fn(FileChooser) -> Fut + Send + Sync + 'static,
350        Fut: std::future::Future<Output = ()> + Send + 'static,
351    {
352        self.event_manager.set_file_chooser_handler(handler).await;
353    }
354
355    /// Wait for a file chooser triggered by an action.
356    ///
357    /// You must call `set_intercept_file_chooser(true)` before using this.
358    ///
359    /// # Example
360    ///
361    /// ```ignore
362    /// page.set_intercept_file_chooser(true).await?;
363    /// let chooser = page.expect_file_chooser(async {
364    ///     page.locator("input[type=file]").click().await?;
365    ///     Ok(())
366    /// }).await?;
367    ///
368    /// chooser.set_files(&["./upload.txt"]).await?;
369    /// ```
370    pub async fn expect_file_chooser<F, Fut>(&self, action: F) -> Result<FileChooser, PageError>
371    where
372        F: FnOnce() -> Fut,
373        Fut: std::future::Future<Output = Result<(), LocatorError>>,
374    {
375        let timeout = DEFAULT_NAVIGATION_TIMEOUT;
376        let chooser_future = self.event_manager.wait_for_file_chooser(timeout);
377        
378        // Perform the action
379        action().await.map_err(|e| PageError::EvaluationFailed(e.to_string()))?;
380        
381        // Wait for the file chooser
382        chooser_future.await
383    }
384}