Archived
254 lines
11 KiB
Rust
254 lines
11 KiB
Rust
//! Tests for el-platform backends.
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::{
|
|
backend_for, backends::{server::ServerBackend, web::WebBackend},
|
|
config::{PlatformConfig, PlatformTarget},
|
|
node::PlatformNode,
|
|
PlatformBackend,
|
|
};
|
|
|
|
// ── Test 1: PlatformTarget::from_str parses all targets ──────────────────
|
|
#[test]
|
|
fn test_platform_target_from_str() {
|
|
assert_eq!(PlatformTarget::from_str("web"), Some(PlatformTarget::Web));
|
|
assert_eq!(PlatformTarget::from_str("server"), Some(PlatformTarget::Server));
|
|
assert_eq!(PlatformTarget::from_str("ios"), Some(PlatformTarget::Ios));
|
|
assert_eq!(PlatformTarget::from_str("android"), Some(PlatformTarget::Android));
|
|
assert_eq!(PlatformTarget::from_str("macos"), Some(PlatformTarget::Macos));
|
|
assert_eq!(PlatformTarget::from_str("linux"), Some(PlatformTarget::Linux));
|
|
assert_eq!(PlatformTarget::from_str("windows"), Some(PlatformTarget::Windows));
|
|
assert_eq!(PlatformTarget::from_str("unknown"), None);
|
|
}
|
|
|
|
// ── Test 2: PlatformTarget::as_str round-trips ───────────────────────────
|
|
#[test]
|
|
fn test_platform_target_as_str() {
|
|
assert_eq!(PlatformTarget::Web.as_str(), "web");
|
|
assert_eq!(PlatformTarget::Server.as_str(), "server");
|
|
assert_eq!(PlatformTarget::Ios.as_str(), "ios");
|
|
}
|
|
|
|
// ── Test 3: is_native() identifies native targets ────────────────────────
|
|
#[test]
|
|
fn test_is_native() {
|
|
assert!(!PlatformTarget::Web.is_native());
|
|
assert!(!PlatformTarget::Server.is_native());
|
|
assert!(PlatformTarget::Ios.is_native());
|
|
assert!(PlatformTarget::Android.is_native());
|
|
assert!(PlatformTarget::Macos.is_native());
|
|
assert!(PlatformTarget::Linux.is_native());
|
|
assert!(PlatformTarget::Windows.is_native());
|
|
}
|
|
|
|
// ── Test 4: PlatformConfig defaults to web ───────────────────────────────
|
|
#[test]
|
|
fn test_platform_config_default() {
|
|
let cfg = PlatformConfig::default();
|
|
assert_eq!(cfg.target, PlatformTarget::Web);
|
|
assert!(!cfg.ssr);
|
|
}
|
|
|
|
// ── Test 5: PlatformConfig with_ssr ──────────────────────────────────────
|
|
#[test]
|
|
fn test_platform_config_with_ssr() {
|
|
let cfg = PlatformConfig::new(PlatformTarget::Server).with_ssr(true);
|
|
assert_eq!(cfg.target, PlatformTarget::Server);
|
|
assert!(cfg.ssr);
|
|
}
|
|
|
|
// ── Test 6: WebBackend renders element to HTML ───────────────────────────
|
|
#[test]
|
|
fn test_web_backend_renders_element() {
|
|
let backend = WebBackend::new();
|
|
let mut div = backend.create_element("div").unwrap();
|
|
backend.set_attribute(&mut div, "class", "container").unwrap();
|
|
backend.append_child(&mut div, backend.create_text("Hello").unwrap()).unwrap();
|
|
|
|
let html = backend.render_to_string(&div).unwrap();
|
|
assert!(html.contains("<div"), "should contain div tag");
|
|
assert!(html.contains("container"), "should contain class name");
|
|
assert!(html.contains("Hello"), "should contain text content");
|
|
assert!(html.contains("</div>"), "should close div tag");
|
|
}
|
|
|
|
// ── Test 7: ServerBackend supports SSR ───────────────────────────────────
|
|
#[test]
|
|
fn test_server_backend_supports_ssr() {
|
|
let backend = ServerBackend::new();
|
|
assert!(backend.supports_ssr());
|
|
}
|
|
|
|
// ── Test 8: ServerBackend renders full HTML page ─────────────────────────
|
|
#[test]
|
|
fn test_server_backend_render_page() {
|
|
let backend = ServerBackend::new();
|
|
let root = PlatformNode::element("div")
|
|
.with_attr("id", "content")
|
|
.with_child(PlatformNode::text("Hello World"));
|
|
|
|
let page = backend.render_page(&root, "My App", "/app.js").unwrap();
|
|
assert!(page.contains("<!DOCTYPE html>"), "should be full HTML doc");
|
|
assert!(page.contains("My App"), "should include title");
|
|
assert!(page.contains("Hello World"), "should include content");
|
|
assert!(page.contains("/app.js"), "should include script src");
|
|
}
|
|
|
|
// ── Test 9: PlatformNode::to_html renders nested tree ────────────────────
|
|
#[test]
|
|
fn test_platform_node_to_html_nested() {
|
|
let node = PlatformNode::element("ul")
|
|
.with_child(PlatformNode::element("li").with_child(PlatformNode::text("item 1")))
|
|
.with_child(PlatformNode::element("li").with_child(PlatformNode::text("item 2")));
|
|
|
|
let html = node.to_html();
|
|
assert!(html.contains("<ul>"));
|
|
assert!(html.contains("<li>item 1</li>"));
|
|
assert!(html.contains("<li>item 2</li>"));
|
|
assert!(html.contains("</ul>"));
|
|
}
|
|
|
|
// ── Test 10: PlatformNode::to_html escapes text content ──────────────────
|
|
#[test]
|
|
fn test_html_escaping() {
|
|
let node = PlatformNode::element("span")
|
|
.with_child(PlatformNode::text("<script>alert('xss')</script>"));
|
|
let html = node.to_html();
|
|
assert!(!html.contains("<script>"), "should escape <script>");
|
|
assert!(html.contains("<script>"), "should contain escaped form");
|
|
}
|
|
|
|
// ── Test 11: Void elements render correctly ───────────────────────────────
|
|
#[test]
|
|
fn test_void_elements() {
|
|
let node = PlatformNode::element("input").with_attr("type", "text");
|
|
let html = node.to_html();
|
|
assert!(html.contains("<input"), "should have input");
|
|
assert!(!html.contains("</input>"), "void element should not have closing tag");
|
|
assert!(html.contains("/>"), "should self-close");
|
|
}
|
|
|
|
// ── Test 12: Fragment node renders children inline ────────────────────────
|
|
#[test]
|
|
fn test_fragment_node() {
|
|
let frag = PlatformNode::fragment()
|
|
.with_child(PlatformNode::element("span").with_child(PlatformNode::text("A")))
|
|
.with_child(PlatformNode::element("span").with_child(PlatformNode::text("B")));
|
|
let html = frag.to_html();
|
|
assert!(html.contains("<span>A</span>"));
|
|
assert!(html.contains("<span>B</span>"));
|
|
// No wrapping element
|
|
assert!(!html.starts_with('<') || html.starts_with("<span>"));
|
|
}
|
|
|
|
// ── Test 13: WebBackend remove_child works ───────────────────────────────
|
|
#[test]
|
|
fn test_web_backend_remove_child() {
|
|
let backend = WebBackend::new();
|
|
let mut parent = PlatformNode::element("div");
|
|
backend
|
|
.append_child(&mut parent, PlatformNode::text("first"))
|
|
.unwrap();
|
|
backend
|
|
.append_child(&mut parent, PlatformNode::text("second"))
|
|
.unwrap();
|
|
assert_eq!(parent.children.len(), 2);
|
|
|
|
backend.remove_child(&mut parent, 0).unwrap();
|
|
assert_eq!(parent.children.len(), 1);
|
|
assert_eq!(parent.children[0].text_content(), Some("second"));
|
|
}
|
|
|
|
// ── Test 14: WebBackend replace_child works ──────────────────────────────
|
|
#[test]
|
|
fn test_web_backend_replace_child() {
|
|
let backend = WebBackend::new();
|
|
let mut parent = PlatformNode::element("div");
|
|
backend
|
|
.append_child(&mut parent, PlatformNode::text("old"))
|
|
.unwrap();
|
|
backend
|
|
.replace_child(&mut parent, 0, PlatformNode::text("new"))
|
|
.unwrap();
|
|
assert_eq!(parent.children[0].text_content(), Some("new"));
|
|
}
|
|
|
|
// ── Test 15: backend_for() returns correct backend names ─────────────────
|
|
#[test]
|
|
fn test_backend_for_names() {
|
|
assert_eq!(backend_for(&PlatformTarget::Web).name(), "web");
|
|
assert_eq!(backend_for(&PlatformTarget::Server).name(), "server");
|
|
assert_eq!(backend_for(&PlatformTarget::Ios).name(), "ios");
|
|
assert_eq!(backend_for(&PlatformTarget::Android).name(), "android");
|
|
assert_eq!(backend_for(&PlatformTarget::Macos).name(), "macos");
|
|
assert_eq!(backend_for(&PlatformTarget::Linux).name(), "linux");
|
|
assert_eq!(backend_for(&PlatformTarget::Windows).name(), "windows");
|
|
}
|
|
|
|
// ── Test 16: WebBackend bind_event emits data attribute ──────────────────
|
|
#[test]
|
|
fn test_web_backend_bind_event() {
|
|
let backend = WebBackend::new();
|
|
let mut btn = backend.create_element("button").unwrap();
|
|
backend
|
|
.bind_event(&mut btn, "click", Box::new(|_| {}))
|
|
.unwrap();
|
|
let has_attr = btn.attributes.iter().any(|a| a.name == "data-el-click");
|
|
assert!(has_attr, "should emit data-el-click attribute");
|
|
}
|
|
|
|
// ── Test 17: ServerBackend bind_event emits hydration marker ─────────────
|
|
#[test]
|
|
fn test_server_backend_bind_event() {
|
|
let backend = ServerBackend::new();
|
|
let mut btn = PlatformNode::element("button");
|
|
backend
|
|
.bind_event(&mut btn, "click", Box::new(|_| {}))
|
|
.unwrap();
|
|
let has_attr = btn.attributes.iter().any(|a| a.name.contains("data-el-click"));
|
|
assert!(has_attr, "should emit hydration marker for click event");
|
|
}
|
|
|
|
// ── Test 18: WebBackend backend does not support raw SSR flag ────────────
|
|
#[test]
|
|
fn test_web_backend_supports_ssr() {
|
|
// Web backend supports ssr (renders to HTML for hydration)
|
|
let backend = WebBackend::new();
|
|
assert!(backend.supports_ssr());
|
|
}
|
|
|
|
// ── Test 19: remove_child out of bounds returns error ────────────────────
|
|
#[test]
|
|
fn test_remove_child_out_of_bounds() {
|
|
let backend = WebBackend::new();
|
|
let mut parent = PlatformNode::element("div");
|
|
let result = backend.remove_child(&mut parent, 5);
|
|
assert!(result.is_err(), "out-of-bounds remove should return error");
|
|
}
|
|
|
|
// ── Test 20: All native backends render to HTML for testing ──────────────
|
|
#[test]
|
|
fn test_native_backends_render_to_html() {
|
|
// Native backends support render_to_string for testing and SSR fallback.
|
|
let targets = [
|
|
PlatformTarget::Ios,
|
|
PlatformTarget::Android,
|
|
PlatformTarget::Macos,
|
|
PlatformTarget::Linux,
|
|
PlatformTarget::Windows,
|
|
];
|
|
for target in &targets {
|
|
let backend = backend_for(target);
|
|
let node = PlatformNode::element("div")
|
|
.with_child(PlatformNode::text("test"));
|
|
let html = backend.render_to_string(&node).unwrap();
|
|
assert!(
|
|
html.contains("test"),
|
|
"{} backend should render text content",
|
|
target.as_str()
|
|
);
|
|
}
|
|
}
|
|
}
|