1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/// Errors that can occur when interacting with the Victauri MCP server from tests.
///
/// Each variant includes actionable context to help diagnose and fix the issue.
#[derive(Debug)]
#[non_exhaustive]
pub enum TestError {
/// Failed to connect to the Victauri MCP server at the expected port.
Connection {
/// Host that was targeted (typically `"127.0.0.1"`).
host: String,
/// Port that was targeted.
port: u16,
/// Human-readable explanation of what went wrong.
reason: String,
},
/// An HTTP-level error occurred during an MCP request.
Request(reqwest::Error),
/// The MCP server returned a JSON-RPC error response.
Mcp {
/// JSON-RPC error code.
code: i64,
/// Human-readable error message from the server.
message: String,
},
/// A tool call returned `isError: true` in its result.
ToolError(String),
/// A test assertion evaluated to false.
Assertion(String),
/// A `wait_for` condition did not become true within the allowed time.
Timeout(String),
/// An element matching the given criteria was not found in the DOM snapshot.
ElementNotFound(String),
/// A visual regression was detected — screenshot differs from baseline.
VisualRegression(String),
/// A catch-all for errors that don't fit other variants (IO, encoding, etc.).
Other(String),
}
impl From<reqwest::Error> for TestError {
fn from(e: reqwest::Error) -> Self {
Self::Request(e)
}
}
impl std::fmt::Display for TestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Connection { host, port, reason } => {
write!(
f,
"connection failed ({host}:{port}): {reason}\n\
\n Possible fixes:\n\
\x20 - Is your Tauri app running? Start it with: pnpm tauri dev\n\
\x20 - Check that victauri-plugin is wired in your Tauri builder\n\
\x20 - Try a different port: VICTAURI_PORT={port} cargo test\n\
\x20 - Run `victauri doctor` for full diagnostics"
)
}
Self::Request(e) => {
write!(f, "MCP request failed: {e}")
}
Self::Mcp { code, message } => {
let hint = match *code {
-32600 => "\n Hint: invalid request — check your MCP protocol version",
-32601 => "\n Hint: method not found — the tool may be disabled by privacy profile",
-32602 => "\n Hint: invalid params — check the tool's expected arguments",
-32603 => "\n Hint: internal error — check the Tauri app's stderr for details",
_ => "",
};
write!(f, "MCP error {code}: {message}{hint}")
}
Self::ToolError(msg) => write!(f, "tool call failed: {msg}"),
Self::Assertion(msg) => write!(f, "assertion failed: {msg}"),
Self::Timeout(msg) => {
write!(
f,
"timeout: {msg}\n\
\n Possible fixes:\n\
\x20 - Increase timeout: .timeout_ms(10_000) or wait_for(..., Some(15_000), ...)\n\
\x20 - Check that the expected condition can actually be met\n\
\x20 - Look for JS errors: client.get_console_logs().await"
)
}
Self::ElementNotFound(msg) => {
write!(
f,
"element not found: {msg}\n\
\n Possible fixes:\n\
\x20 - Take a DOM snapshot to see what's on the page: client.dom_snapshot().await\n\
\x20 - The element may not have rendered yet — use expect().to_be_visible().await\n\
\x20 - Check for typos in the locator query"
)
}
Self::VisualRegression(msg) => {
write!(
f,
"visual regression: {msg}\n\
\n If the change is intentional, delete the baseline image to regenerate it.\n\
\x20 Use ThresholdPreset::Relaxed for cross-platform tolerance."
)
}
Self::Other(msg) => write!(f, "{msg}"),
}
}
}
impl std::error::Error for TestError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Request(e) => Some(e),
_ => None,
}
}
}