viewpoint_core/page/page_error/mod.rs
1//! Page error types and event handling.
2//!
3//! This module provides types for capturing uncaught JavaScript exceptions
4//! and errors from the page.
5
6use viewpoint_cdp::protocol::runtime::{ExceptionDetails, ExceptionThrownEvent};
7
8/// An uncaught exception from the page.
9///
10/// Page errors are emitted when JavaScript code throws an uncaught exception.
11/// These correspond to the 'pageerror' event in Playwright.
12///
13/// # Example
14///
15/// ```
16/// # #[cfg(feature = "integration")]
17/// # tokio_test::block_on(async {
18/// # use viewpoint_core::Browser;
19/// # let browser = Browser::launch().headless(true).launch().await.unwrap();
20/// # let context = browser.new_context().await.unwrap();
21/// # let page = context.new_page().await.unwrap();
22///
23/// page.on_pageerror(|error| async move {
24/// println!("Page error: {}", error.message());
25/// }).await;
26/// # });
27/// ```
28#[derive(Debug, Clone)]
29pub struct PageError {
30 /// Exception details.
31 exception_details: ExceptionDetails,
32 /// Timestamp when the error occurred.
33 timestamp: f64,
34}
35
36impl PageError {
37 /// Create a new page error from a CDP event.
38 pub(crate) fn from_event(event: ExceptionThrownEvent) -> Self {
39 Self {
40 exception_details: event.exception_details,
41 timestamp: event.timestamp,
42 }
43 }
44
45 /// Get the error message.
46 pub fn message(&self) -> String {
47 // Try to get the exception message first
48 if let Some(ref exception) = self.exception_details.exception {
49 if let Some(ref description) = exception.description {
50 return description.clone();
51 }
52 if let Some(ref value) = exception.value {
53 if let Some(s) = value.as_str() {
54 return s.to_string();
55 }
56 return value.to_string();
57 }
58 }
59
60 // Fall back to the exception text
61 self.exception_details.text.clone()
62 }
63
64 /// Get the full stack trace as a string.
65 pub fn stack(&self) -> Option<String> {
66 self.exception_details.exception.as_ref().and_then(|exc| {
67 exc.description.clone()
68 })
69 }
70
71 /// Get the error name (e.g., "`TypeError`", "`ReferenceError`").
72 pub fn name(&self) -> Option<String> {
73 self.exception_details.exception.as_ref().and_then(|exc| {
74 exc.class_name.clone()
75 })
76 }
77
78 /// Get the URL where the error occurred.
79 pub fn url(&self) -> Option<&str> {
80 self.exception_details.url.as_deref()
81 }
82
83 /// Get the line number where the error occurred.
84 pub fn line_number(&self) -> i64 {
85 self.exception_details.line_number
86 }
87
88 /// Get the column number where the error occurred.
89 pub fn column_number(&self) -> i64 {
90 self.exception_details.column_number
91 }
92
93 /// Get the timestamp when the error occurred.
94 pub fn timestamp(&self) -> f64 {
95 self.timestamp
96 }
97}
98
99impl std::fmt::Display for PageError {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 if let Some(name) = self.name() {
102 write!(f, "{}: {}", name, self.message())
103 } else {
104 write!(f, "{}", self.message())
105 }
106 }
107}
108
109impl std::error::Error for PageError {}
110
111/// An error from any page in a browser context.
112///
113/// Web errors are emitted at the context level when any page has an uncaught
114/// exception. They include a reference to the page where the error occurred.
115///
116/// # Example
117///
118/// ```
119/// # #[cfg(feature = "integration")]
120/// # tokio_test::block_on(async {
121/// # use viewpoint_core::Browser;
122/// # let browser = Browser::launch().headless(true).launch().await.unwrap();
123/// # let context = browser.new_context().await.unwrap();
124///
125/// context.on_weberror(|error| async move {
126/// println!("Error: {}", error.message());
127/// }).await;
128/// # });
129/// ```
130#[derive(Debug, Clone)]
131pub struct WebError {
132 /// The underlying page error.
133 error: PageError,
134 /// Target ID of the page where the error occurred.
135 target_id: String,
136 /// Session ID of the page.
137 session_id: String,
138}
139
140impl WebError {
141 /// Create a new web error.
142 pub(crate) fn new(error: PageError, target_id: String, session_id: String) -> Self {
143 Self {
144 error,
145 target_id,
146 session_id,
147 }
148 }
149
150 /// Get the error message.
151 pub fn message(&self) -> String {
152 self.error.message()
153 }
154
155 /// Get the full stack trace as a string.
156 pub fn stack(&self) -> Option<String> {
157 self.error.stack()
158 }
159
160 /// Get the error name.
161 pub fn name(&self) -> Option<String> {
162 self.error.name()
163 }
164
165 /// Get the target ID of the page where the error occurred.
166 pub fn target_id(&self) -> &str {
167 &self.target_id
168 }
169
170 /// Get the session ID of the page.
171 pub fn session_id(&self) -> &str {
172 &self.session_id
173 }
174
175 /// Get the underlying page error.
176 pub fn page_error(&self) -> &PageError {
177 &self.error
178 }
179}
180
181impl std::fmt::Display for WebError {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(f, "{}", self.error)
184 }
185}
186
187impl std::error::Error for WebError {}