Skip to content
Merged

234 #320

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 183 additions & 1 deletion src/frontend/browser_console_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,152 @@
//
// SPDX-License-Identifier: MIT

//! Browser console error types.
//!
//! This module defines [`BrowserConsoleError`], which represents failures
//! when attempting to serialize errors or log them to the browser console.
//!
//! # Error Variants
//!
//! - [`BrowserConsoleError::Serialization`] - Serialization to JsValue failed
//! - [`BrowserConsoleError::ConsoleUnavailable`] - Console object not
//! accessible
//! - [`BrowserConsoleError::ConsoleErrorUnavailable`] - console.error not
//! accessible
//! - [`BrowserConsoleError::ConsoleMethodNotCallable`] - console.error not
//! callable
//! - [`BrowserConsoleError::ConsoleInvocation`] - console.error invocation
//! failed
//! - [`BrowserConsoleError::UnsupportedTarget`] - Not a WASM target
//!
//! # Examples
//!
//! ```
//! use masterror::frontend::BrowserConsoleError;
//!
//! let err = BrowserConsoleError::Serialization {
//! message: "invalid JSON".to_owned()
//! };
//! assert_eq!(err.context(), Some("invalid JSON"));
//! ```

use crate::Error;

/// Error returned when emitting to the browser console fails or is unsupported.
#[derive(Debug, Error, PartialEq, Eq)]
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::UnsupportedTarget;
/// assert_eq!(
/// err.to_string(),
/// "browser console logging is not supported on this target"
/// );
///
/// let err = BrowserConsoleError::ConsoleMethodNotCallable;
/// assert!(err.to_string().contains("not callable"));
/// ```
#[derive(Debug, Error, Clone, PartialEq, Eq)]

Check warning on line 52 in src/frontend/browser_console_error.rs

View check run for this annotation

Codecov / codecov/patch

src/frontend/browser_console_error.rs#L52

Added line #L52 was not covered by tests
#[cfg_attr(docsrs, doc(cfg(feature = "frontend")))]
pub enum BrowserConsoleError {
/// Failed to serialize the payload into [`wasm_bindgen::JsValue`].
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::Serialization {
/// message: "JSON error".to_owned()
/// };
/// assert_eq!(err.context(), Some("JSON error"));
/// assert!(err.to_string().contains("failed to serialize"));
/// ```
#[error("failed to serialize payload for browser console: {message}")]
Serialization {
/// Human-readable description of the serialization failure.
message: String
},

/// The global `console` object is unavailable or could not be accessed.
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::ConsoleUnavailable {
/// message: "console is null".to_owned()
/// };
/// assert_eq!(err.context(), Some("console is null"));
/// ```
#[error("browser console object is not available: {message}")]
ConsoleUnavailable {
/// Additional context explaining the failure.
message: String
},

/// The `console.error` function is missing or not accessible.
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::ConsoleErrorUnavailable {
/// message: "error method undefined".to_owned()
/// };
/// assert_eq!(err.context(), Some("error method undefined"));
/// ```
#[error("failed to access browser console `error`: {message}")]
ConsoleErrorUnavailable {
/// Additional context explaining the failure.
message: String
},

/// The retrieved `console.error` value is not callable.
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::ConsoleMethodNotCallable;
/// assert_eq!(err.context(), None);
/// ```
#[error("browser console `error` method is not callable")]
ConsoleMethodNotCallable,

/// Invoking `console.error` returned an error.
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::ConsoleInvocation {
/// message: "TypeError".to_owned()
/// };
/// assert_eq!(err.context(), Some("TypeError"));
/// ```
#[error("failed to invoke browser console `error`: {message}")]
ConsoleInvocation {
/// Textual representation of the JavaScript exception.
message: String
},

/// Logging is not supported on the current compilation target.
///
/// # Examples
///
/// ```
/// use masterror::frontend::BrowserConsoleError;
///
/// let err = BrowserConsoleError::UnsupportedTarget;
/// assert_eq!(err.context(), None);
/// ```
#[error("browser console logging is not supported on this target")]
UnsupportedTarget
}
Expand Down Expand Up @@ -81,3 +193,73 @@
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn clone_creates_identical_copy() {
let err1 = BrowserConsoleError::Serialization {
message: "test".to_string()
};
let err2 = err1.clone();
assert_eq!(err1, err2);

let err3 = BrowserConsoleError::ConsoleMethodNotCallable;
let err4 = err3.clone();
assert_eq!(err3, err4);
}

#[test]
fn context_returns_none_for_unit_variants() {
assert_eq!(
BrowserConsoleError::ConsoleMethodNotCallable.context(),
None
);
assert_eq!(BrowserConsoleError::UnsupportedTarget.context(), None);
}

#[test]
fn context_returns_message_for_serialization() {
let err = BrowserConsoleError::Serialization {
message: "JSON parse error".to_string()
};
assert_eq!(err.context(), Some("JSON parse error"));
}

#[test]
fn context_returns_message_for_console_invocation() {
let err = BrowserConsoleError::ConsoleInvocation {
message: "TypeError: null reference".to_string()
};
assert_eq!(err.context(), Some("TypeError: null reference"));
}

#[test]
fn partial_eq_compares_variants_correctly() {
let serialization1 = BrowserConsoleError::Serialization {
message: "error1".to_string()
};
let serialization2 = BrowserConsoleError::Serialization {
message: "error1".to_string()
};
let serialization3 = BrowserConsoleError::Serialization {
message: "error2".to_string()
};

assert_eq!(serialization1, serialization2);
assert_ne!(serialization1, serialization3);
assert_ne!(serialization1, BrowserConsoleError::UnsupportedTarget);
}

#[test]
fn debug_format_includes_variant_and_message() {
let err = BrowserConsoleError::ConsoleUnavailable {
message: "console is undefined".to_string()
};
let debug_str = format!("{err:?}");
assert!(debug_str.contains("ConsoleUnavailable"));
assert!(debug_str.contains("console is undefined"));
}
}
68 changes: 68 additions & 0 deletions src/frontend/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,74 @@ fn partial_eq_works() {
assert_ne!(err1, err3);
}

#[test]
fn serialization_error_with_empty_message() {
let err = BrowserConsoleError::Serialization {
message: String::new()
};
assert_eq!(err.context(), Some(""));
assert!(err.to_string().contains("failed to serialize"));
}

#[test]
fn console_unavailable_with_unicode() {
let err = BrowserConsoleError::ConsoleUnavailable {
message: "コンソールなし".to_owned()
};
assert_eq!(err.context(), Some("コンソールなし"));
}

#[test]
fn console_error_unavailable_with_long_message() {
let long_msg = "x".repeat(1000);
let err = BrowserConsoleError::ConsoleErrorUnavailable {
message: long_msg.clone()
};
assert_eq!(err.context(), Some(long_msg.as_str()));
}

#[test]
fn console_invocation_with_special_chars() {
let err = BrowserConsoleError::ConsoleInvocation {
message: "Error: \"test\" <>&".to_owned()
};
assert_eq!(err.context(), Some("Error: \"test\" <>&"));
}

#[test]
fn clone_works_for_error_variants() {
let err1 = BrowserConsoleError::Serialization {
message: "test".to_owned()
};
let err2 = err1.clone();
assert_eq!(err1, err2);
}

#[test]
fn eq_compares_messages() {
let err1 = BrowserConsoleError::Serialization {
message: "msg1".to_owned()
};
let err2 = BrowserConsoleError::Serialization {
message: "msg1".to_owned()
};
let err3 = BrowserConsoleError::Serialization {
message: "msg2".to_owned()
};

assert_eq!(err1, err2);
assert_ne!(err1, err3);
}

#[test]
fn context_returns_none_for_unit_variants() {
assert_eq!(
BrowserConsoleError::ConsoleMethodNotCallable.context(),
None
);
assert_eq!(BrowserConsoleError::UnsupportedTarget.context(), None);
}

#[cfg(not(target_arch = "wasm32"))]
mod native {
use super::*;
Expand Down
Loading