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}