use iced::Theme;
use serde_json::Value;
use crate::image_registry::ImageRegistry;
use crate::protocol::{Props, TreeNode};
use crate::registry::WidgetRegistry;
use crate::render_ctx::RenderCtx;
use crate::shared_state::SharedState;
use crate::theming::ThemeChrome;
pub fn node(id: &str, type_name: &str) -> TreeNode {
TreeNode {
id: id.to_string(),
type_name: type_name.to_string(),
props: Props::default(),
children: vec![],
}
}
pub fn node_with_props(id: &str, type_name: &str, props: Value) -> TreeNode {
TreeNode {
id: id.to_string(),
type_name: type_name.to_string(),
props: Props::from_json(props),
children: vec![],
}
}
pub fn node_with_children(id: &str, type_name: &str, children: Vec<TreeNode>) -> TreeNode {
TreeNode {
id: id.to_string(),
type_name: type_name.to_string(),
props: Props::default(),
children,
}
}
pub fn node_with_props_and_children(
id: &str,
type_name: &str,
props: Value,
children: Vec<TreeNode>,
) -> TreeNode {
TreeNode {
id: id.to_string(),
type_name: type_name.to_string(),
props: Props::from_json(props),
children,
}
}
pub struct TestEnv {
pub shared_state: SharedState,
pub images: ImageRegistry,
pub theme: Theme,
pub theme_chrome: ThemeChrome,
pub registry: WidgetRegistry,
pub default_text_size: Option<f32>,
pub default_font: Option<iced::Font>,
}
impl std::fmt::Debug for TestEnv {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TestEnv")
.field("images", &self.images)
.field("registry", &self.registry)
.field("default_text_size", &self.default_text_size)
.field("default_font", &self.default_font)
.finish_non_exhaustive()
}
}
impl Default for TestEnv {
fn default() -> Self {
let mut registry = WidgetRegistry::new();
registry.register_set(&crate::widget::widget_set::iced_widget_set());
Self {
shared_state: SharedState::new(),
images: ImageRegistry::new(),
theme: Theme::Dark,
theme_chrome: ThemeChrome::default(),
registry,
default_text_size: None,
default_font: None,
}
}
}
impl TestEnv {
pub fn render_ctx(&self) -> RenderCtx<'_> {
RenderCtx {
caches: &self.shared_state,
images: &self.images,
theme: &self.theme,
theme_chrome: self.theme_chrome,
registry: &self.registry,
default_text_size: self.default_text_size,
default_font: self.default_font,
window_id: "",
scale_factor: 1.0,
validate_props: crate::validate::is_validate_props_enabled(),
}
}
pub fn prepare_and_render<'a, W>(
&'a self,
widget: &'a mut W,
node: &'a TreeNode,
window_id: &str,
) -> iced::Element<'a, crate::runtime::Message, iced::Theme, iced::Renderer>
where
W: crate::registry::PlushieWidget<iced::Renderer>,
{
widget.prepare(node, window_id, &self.theme);
let ctx = self.render_ctx();
widget.render(node, &ctx)
}
pub fn handle_message_events<W>(
&self,
widget: &mut W,
msg: &crate::runtime::Message,
) -> Vec<crate::protocol::OutgoingEvent>
where
W: crate::registry::PlushieWidget<iced::Renderer>,
{
match widget.handle_message(msg) {
crate::registry::HandleResult::Handled(v) => v,
crate::registry::HandleResult::Fallthrough => Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prop_helpers::{prop_f32, prop_str};
use crate::registry::GenerationCounter;
use serde_json::json;
#[test]
fn node_has_empty_props_and_no_children() {
let n = node("btn-1", "button");
assert_eq!(n.id, "btn-1");
assert_eq!(n.type_name, "button");
assert!(n.children.is_empty());
assert_eq!(n.props.to_value(), json!({}));
}
#[test]
fn node_with_props_stores_props() {
let n = node_with_props("txt-1", "text", json!({"content": "hello", "size": 14}));
assert_eq!(n.props.to_value()["content"], "hello");
assert_eq!(n.props.to_value()["size"], 14);
}
#[test]
fn node_with_children_stores_children() {
let children = vec![node("a", "text"), node("b", "button")];
let n = node_with_children("col-1", "column", children);
assert_eq!(n.children.len(), 2);
assert_eq!(n.children[0].id, "a");
assert_eq!(n.children[1].id, "b");
}
#[test]
fn node_props_work_with_prop_helpers() {
let n = node_with_props("s-1", "sparkline", json!({"label": "cpu", "max": 100.0}));
assert_eq!(prop_str(&n.props, "label"), Some("cpu".to_string()));
assert!((prop_f32(&n.props, "max").unwrap() - 100.0).abs() < 0.001);
}
#[test]
fn default_env_has_no_text_defaults() {
let test = TestEnv::default();
let ctx = test.render_ctx();
assert!(ctx.default_text_size.is_none());
assert!(ctx.default_font.is_none());
}
#[test]
fn env_inherits_text_defaults() {
let test = TestEnv {
default_text_size: Some(18.0),
default_font: Some(iced::Font::MONOSPACE),
..TestEnv::default()
};
let ctx = test.render_ctx();
assert_eq!(ctx.default_text_size, Some(18.0));
assert_eq!(ctx.default_font, Some(iced::Font::MONOSPACE));
}
#[test]
fn env_theme_is_customizable() {
let _test = TestEnv {
theme: Theme::Light,
..TestEnv::default()
};
}
#[test]
fn generation_counter_lifecycle() {
let mut counter = GenerationCounter::new();
assert_eq!(counter.get(), 0);
counter.bump();
counter.bump();
assert_eq!(counter.get(), 2);
}
}