earl_protocol_browser/
error.rs1#[derive(Debug, thiserror::Error)]
2pub enum BrowserError {
3 #[error(
4 "browser step {step} ({action}) failed: element not found — {selector} — completed {completed} of {total} steps"
5 )]
6 ElementNotFound {
7 step: usize,
8 action: String,
9 selector: String,
10 completed: usize,
11 total: usize,
12 },
13
14 #[error(
15 "browser step {step} ({action}) failed: element not interactable — {selector} — completed {completed} of {total} steps"
16 )]
17 ElementNotInteractable {
18 step: usize,
19 action: String,
20 selector: String,
21 completed: usize,
22 total: usize,
23 },
24
25 #[error("browser step {step} (navigate) failed: {reason}")]
26 NavigationFailed { step: usize, reason: String },
27
28 #[error("browser step {step} ({action}) assertion failed: {message}")]
29 AssertionFailed {
30 step: usize,
31 action: String,
32 message: String,
33 },
34
35 #[error("browser renderer crashed at step {step}")]
36 RendererCrashed { step: usize },
37
38 #[error(
39 "browser step {step}: a dialog is blocking the page — add a handle_dialog step before this one"
40 )]
41 DialogBlocking { step: usize },
42
43 #[error(
44 "browser step {step}: a download was triggered — use a download step with save_to to handle it explicitly"
45 )]
46 DownloadBlocked { step: usize },
47
48 #[error(
49 "browser step {step} (click): a new tab was opened — add a tabs step with operation=\"select\" to switch to it"
50 )]
51 NewTabOpened { step: usize },
52
53 #[error("browser step {step} ({action}) timed out after {timeout_ms}ms")]
54 Timeout {
55 step: usize,
56 action: String,
57 timeout_ms: u64,
58 },
59
60 #[error("ref \"{ref_id}\" no longer exists in the accessibility tree (used in {action})")]
61 StaleRef { ref_id: String, action: String },
62
63 #[error(
64 "URL scheme \"{scheme}\" is not allowed in navigate — only http and https are permitted"
65 )]
66 DisallowedScheme { scheme: String },
67
68 #[error("browser session is locked by another earl process (PID {pid})")]
71 SessionLocked { session_id: String, pid: u32 },
72
73 #[error(
74 "Chrome not found — install Chrome/Chromium or set EARL_BROWSER_PATH\nPaths tried:\n{paths}"
75 )]
76 ChromeNotFound { paths: String },
77
78 #[error("browser session lost during step {step} ({action}): {reason}")]
79 SessionLost {
80 step: usize,
81 action: String,
82 reason: String,
83 },
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn error_messages_include_context() {
92 let e = BrowserError::ElementNotFound {
93 step: 2,
94 action: "click".into(),
95 selector: "#submit".into(),
96 completed: 1,
97 total: 5,
98 };
99 let msg = e.to_string();
100 assert!(msg.contains("step 2"));
101 assert!(msg.contains("click"));
102 assert!(msg.contains("#submit"));
103 assert!(msg.contains("completed 1 of 5"));
104
105 let e2 = BrowserError::DisallowedScheme {
106 scheme: "file".into(),
107 };
108 assert!(e2.to_string().contains("file"));
109 assert!(e2.to_string().contains("http"));
110 }
111}