///|
/// CDP Testing Utilities
///
/// Test helpers for CDP domain tests.
/// Pattern inspired by lightpanda-browser testing.zig
///|
/// Test context for CDP operations
pub struct TestContext {
session : CdpSession
}
///|
/// Create a new test context
pub fn TestContext::new() -> TestContext {
{ session: CdpSession::new("test-session") }
}
///|
/// Create test context with HTML content
pub fn TestContext::with_html(html : String) -> TestContext {
let ctx = TestContext::new()
// Parse HTML and build DOM tree
ctx.parse_html(html)
ctx
}
///|
/// Parse simple HTML into DOM tree
/// Supports: , , text, nested elements
fn TestContext::parse_html(self : TestContext, html : String) -> Unit {
let tree = self.session.get_tree()
let doc = tree.get_document()
// Create html > body structure
let html_elem = tree.create_element("html")
let body = tree.create_element("body")
let _ = tree.append_child(doc, html_elem)
let _ = tree.append_child(html_elem, body)
// Parse content into body
parse_html_content(tree, body, html)
}
///|
/// Simple HTML parser (for testing only)
fn parse_html_content(
tree : @dom.DomTree,
parent : @dom.NodeId,
html : String,
) -> Unit {
let chars = html.to_array()
let len = chars.length()
let i = Ref(0)
while i.val < len {
if chars[i.val] == '<' {
// Check for closing tag
if i.val + 1 < len && chars[i.val + 1] == '/' {
// Skip closing tag
while i.val < len && chars[i.val] != '>' {
i.val += 1
}
i.val += 1
continue
}
// Parse opening tag
i.val += 1
let tag_start = i.val
while i.val < len && chars[i.val] != ' ' && chars[i.val] != '>' {
i.val += 1
}
let tag_name = substring(chars, tag_start, i.val)
let elem = tree.create_element(tag_name)
let _ = tree.append_child(parent, elem)
// Parse attributes
while i.val < len && chars[i.val] != '>' {
// Skip whitespace
while i.val < len && chars[i.val] == ' ' {
i.val += 1
}
if i.val < len && chars[i.val] != '>' {
// Parse attribute name
let attr_start = i.val
while i.val < len &&
chars[i.val] != '=' &&
chars[i.val] != ' ' &&
chars[i.val] != '>' {
i.val += 1
}
let attr_name = substring(chars, attr_start, i.val)
if i.val < len && chars[i.val] == '=' {
i.val += 1
// Skip quote
if i.val < len && chars[i.val] == '"' {
i.val += 1
}
let val_start = i.val
while i.val < len && chars[i.val] != '"' {
i.val += 1
}
let attr_val = substring(chars, val_start, i.val)
if i.val < len && chars[i.val] == '"' {
i.val += 1
}
let _ = tree.set_attribute(elem, attr_name, attr_val)
}
}
}
if i.val < len && chars[i.val] == '>' {
i.val += 1
}
// Parse children (recursively find content until closing tag)
let content_start = i.val
let depth = Ref(1)
while i.val < len && depth.val > 0 {
if chars[i.val] == '<' {
if i.val + 1 < len && chars[i.val + 1] == '/' {
depth.val -= 1
if depth.val == 0 {
break
}
} else if i.val + 1 < len && chars[i.val + 1] != '!' {
depth.val += 1
}
}
i.val += 1
}
let inner = substring(chars, content_start, i.val)
if inner.length() > 0 {
parse_html_content(tree, elem, inner)
}
// Skip closing tag
while i.val < len && chars[i.val] != '>' {
i.val += 1
}
i.val += 1
} else {
// Text content
let text_start = i.val
while i.val < len && chars[i.val] != '<' {
i.val += 1
}
let text = substring(chars, text_start, i.val).trim().to_owned()
if text.length() > 0 {
let text_node = tree.create_text(text)
let _ = tree.append_child(parent, text_node)
}
}
}
}
///|
/// Extract substring from char array
fn substring(chars : Array[Char], start : Int, end : Int) -> String {
let buf = StringBuilder::new()
for i = start; i < end; i = i + 1 {
buf.write_char(chars[i])
}
buf.to_string()
}
///|
/// Get DOM domain from test context
pub fn TestContext::dom(self : TestContext) -> DomDomain {
self.session.get_dom()
}
///|
/// Get Page domain from test context
pub fn TestContext::page(self : TestContext) -> PageDomain {
self.session.get_page()
}
///|
/// Get Input domain from test context
pub fn TestContext::input(self : TestContext) -> InputDomain {
self.session.get_input()
}
///|
/// Get the underlying DOM tree
pub fn TestContext::tree(self : TestContext) -> @dom.DomTree {
self.session.get_tree()
}
///|
/// Get document root node ID
pub fn TestContext::doc_id(self : TestContext) -> Int {
self.session.get_tree().get_document().to_int()
}