diff --git a/src/frontend/browser_console_error.rs b/src/frontend/browser_console_error.rs index f469824..7abfbdc 100644 --- a/src/frontend/browser_console_error.rs +++ b/src/frontend/browser_console_error.rs @@ -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)] #[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 } @@ -81,3 +193,73 @@ impl BrowserConsoleError { } } } + +#[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")); + } +} diff --git a/src/frontend/tests.rs b/src/frontend/tests.rs index 97bd1ed..23f91f0 100644 --- a/src/frontend/tests.rs +++ b/src/frontend/tests.rs @@ -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::*;