use rustc_hash::FxHashMap;
use serde_json::json;
use super::{
escape_svelte_markup, render_framework_code, render_framework_component_code,
FrameworkCodegenError, FrameworkCodegenMode, FrameworkCodegenTarget, FrameworkComponentIsland,
};
#[test]
fn renders_react_create_element_code() {
let code = render_framework_component_code(
&[
r#"<section class="lead" for="name" data-id="42" aria-label="Intro">"#,
r#"<p style="font-weight: bold; --brand: red;">Hello <strong>world</strong></p>"#,
"</section>",
]
.join(""),
FrameworkCodegenTarget::React,
&[],
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_vue_h_code() {
let code = render_framework_component_code(
r#"<label class="field" for="name"><span>Name</span><input disabled type="text"></label>"#,
FrameworkCodegenTarget::Vue,
&[],
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_void_and_self_closing_elements_without_children() {
let code = render_framework_component_code(
r#"<p>Image <img src="/logo.png"><br/><input disabled></p>"#,
FrameworkCodegenTarget::React,
&[],
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_escaped_js_string_literals_for_text_and_attributes() {
let code = render_framework_component_code(
"<p title=\"quote " slash \\\">line\nnext</p>",
FrameworkCodegenTarget::React,
&[],
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_react_data_and_aria_camel_case_as_attributes() {
let code = render_framework_component_code(
r#"<button dataTestId="save" ariaLabel="Save">Save</button>"#,
FrameworkCodegenTarget::React,
&[],
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_vue_single_child_without_array_and_multiple_children_with_array() {
let single = render_framework_component_code(
"<p><span>one</span></p>",
FrameworkCodegenTarget::Vue,
&[],
);
let many = render_framework_component_code(
"<p><span>one</span><span>two</span></p>",
FrameworkCodegenTarget::Vue,
&[],
);
insta::assert_snapshot!(format!("single:\n{single}\n\nmany:\n{many}"));
}
#[test]
fn renders_framework_islands_with_deterministic_props() {
let mut props = FxHashMap::default();
props.insert("tone".to_string(), json!("info"));
props.insert("active".to_string(), json!(true));
let islands = vec![FrameworkComponentIsland {
id: "ox-island-0".to_string(),
name: "Alert".to_string(),
props,
content: Some("Read docs".to_string()),
}];
let code = render_framework_component_code(
r#"<p>Before</p><div data-ox-island="Alert" data-ox-id="ox-island-0"></div>"#,
FrameworkCodegenTarget::React,
&islands,
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_islands_from_camel_case_marker_and_skips_marker_props() {
let mut props = FxHashMap::default();
props.insert("count".into(), json!(3));
let islands = vec![FrameworkComponentIsland {
id: "ox-island-0".into(),
name: "Counter".into(),
props,
content: None,
}];
let code = render_framework_component_code(
r#"<div dataOxId="ox-island-0" data-ox-island="Counter"></div>"#,
FrameworkCodegenTarget::Vue,
&islands,
);
assert_eq!(code, r#"h('div', { class: 'ox-content' }, h(Counter, { "count": 3 }))"#);
}
#[test]
fn renders_nested_json_props_in_stable_top_level_order() {
let mut props = FxHashMap::default();
props.insert("z".into(), json!([3, 2, 1]));
props.insert("a".into(), json!({ "nested": true }));
let islands = vec![FrameworkComponentIsland {
id: "ox-island-0".into(),
name: "Widget".into(),
props,
content: Some("child".into()),
}];
let code = render_framework_component_code(
r#"<div data-ox-id="ox-island-0"></div>"#,
FrameworkCodegenTarget::React,
&islands,
);
insta::assert_snapshot!(code);
}
#[test]
fn renders_inner_html_component_modules() {
let react = render_framework_code(
"<p>Hello</p>",
FrameworkCodegenTarget::React,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
let vue = render_framework_code(
"<p>Hello</p>",
FrameworkCodegenTarget::Vue,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
insta::assert_snapshot!(format!("react:\n{react}\n\nvue:\n{vue}"));
}
#[test]
fn escapes_raw_html_literals_without_changing_runtime_html() {
let html = "</script><p title=\"line\nnext\">{ ok }</p>";
let react = render_framework_code(
html,
FrameworkCodegenTarget::React,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
let vue = render_framework_code(
html,
FrameworkCodegenTarget::Vue,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
let svelte = render_framework_code(
html,
FrameworkCodegenTarget::Svelte,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
insta::assert_snapshot!(format!("react:\n{react}\n\nvue:\n{vue}\n\nsvelte:\n{svelte}"));
}
#[test]
fn renders_component_and_render_function_modules() {
let component = render_framework_code(
"<p>Hello</p>",
FrameworkCodegenTarget::React,
FrameworkCodegenMode::Component,
&[],
)
.unwrap();
let render_function = render_framework_code(
"<p>Hello</p>",
FrameworkCodegenTarget::Vue,
FrameworkCodegenMode::RenderFunction,
&[],
)
.unwrap();
insta::assert_snapshot!(format!(
"component:\n{component}\n\nrender_function:\n{render_function}"
));
}
#[test]
fn renders_svelte_component_modes() {
let component = render_framework_code(
"<p>{count}</p>",
FrameworkCodegenTarget::Svelte,
FrameworkCodegenMode::Component,
&[],
)
.unwrap();
let inner_html = render_framework_code(
"<p>{count}</p>",
FrameworkCodegenTarget::Svelte,
FrameworkCodegenMode::InnerHtml,
&[],
)
.unwrap();
insta::assert_snapshot!(format!("component:\n{component}\n\ninner_html:\n{inner_html}"));
}
#[test]
fn rejects_svelte_render_function_mode() {
let error = render_framework_code(
"<p>Hello</p>",
FrameworkCodegenTarget::Svelte,
FrameworkCodegenMode::RenderFunction,
&[],
)
.unwrap_err();
assert_eq!(
error,
FrameworkCodegenError::UnsupportedModeForTarget {
mode: FrameworkCodegenMode::RenderFunction,
target: FrameworkCodegenTarget::Svelte,
}
);
}
#[test]
fn escapes_svelte_expression_delimiters() {
assert_eq!(escape_svelte_markup("<p>{count} and }</p>"), "<p>{count} and }</p>");
}