pub mod devserver;
pub mod server;
use std::collections::HashMap;
use crate::model::TestFailure;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ComponentRef {
pub id: String,
#[serde(default)]
pub props: serde_json::Value,
#[serde(default)]
pub children: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct MountOptions {
#[serde(default)]
pub props: serde_json::Value,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub hooks_config: HashMap<String, serde_json::Value>,
}
pub async fn mount(
page: &std::sync::Arc<ferridriver::Page>,
_base_url: &str,
component: &ComponentRef,
options: &MountOptions,
) -> Result<ferridriver::Locator, TestFailure> {
let payload = serde_json::json!({
"component": component,
"options": options,
});
let escaped_json = payload.to_string().replace('\\', "\\\\").replace('`', "\\`");
let js = format!(
r#"(() => {{
const data = JSON.parse(`{escaped_json}`);
const root = document.getElementById('root') || document.getElementById('app');
if (!root) throw new Error('No #root or #app element found');
window.__ferriMount(data.component, root, data.options);
return root.innerHTML;
}})()"#,
);
let eval_result = page
.evaluate(&js, ferridriver::protocol::SerializedArgument::default(), None)
.await;
eval_result.map_err(|e| TestFailure {
message: format!("mount failed: {e}"),
stack: None,
diff: None,
screenshot: None,
})?;
Ok(page.locator("#root, #app", None))
}
pub async fn unmount(page: &std::sync::Arc<ferridriver::Page>) -> Result<(), TestFailure> {
page
.evaluate(
"() => { if (window.__ferriUnmount) window.__ferriUnmount(); }",
ferridriver::protocol::SerializedArgument::default(),
None,
)
.await
.map_err(|e| TestFailure {
message: format!("unmount failed: {e}"),
stack: None,
diff: None,
screenshot: None,
})?;
Ok(())
}
pub const INJECTED_REGISTRY_JS: &str = r#"
// ferridriver CT: import registry + component deserializer.
window.__ferriRegistry = {};
window.__ferriRegister = function(id, importFn) {
window.__ferriRegistry[id] = importFn;
};
// Resolve an importRef to the actual module.
window.__ferriResolve = async function(ref) {
const loader = window.__ferriRegistry[ref.id];
if (!loader) throw new Error(`Component not registered: ${ref.id}`);
return await loader();
};
"#;