diff --git a/crates/obscura-cdp/tests/treewalker_document_order.rs b/crates/obscura-cdp/tests/treewalker_document_order.rs new file mode 100644 index 0000000..2808601 --- /dev/null +++ b/crates/obscura-cdp/tests/treewalker_document_order.rs @@ -0,0 +1,152 @@ +use obscura_cdp::dispatch::{dispatch, CdpContext}; +use obscura_cdp::types::CdpRequest; +use serde_json::{json, Value}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; + +async fn serve_once() -> String { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + tokio::spawn(async move { + let (mut socket, _) = listener.accept().await.unwrap(); + let mut buf = [0u8; 2048]; + let _ = socket.read(&mut buf).await.unwrap(); + let body = "
"; + let response = format!( + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", + body.len() + ); + let _ = socket.write_all(response.as_bytes()).await; + }); + format!("http://{addr}/") +} + +async fn cdp( + ctx: &mut CdpContext, + id: u64, + method: &str, + params: Value, + session_id: &str, +) -> Value { + let response = dispatch( + &CdpRequest { + id, + method: method.to_string(), + params, + session_id: Some(session_id.to_string()), + }, + ctx, + ) + .await; + assert!( + response.error.is_none(), + "CDP {method} failed: {:?}", + response.error + ); + response.result.unwrap_or_else(|| json!({})) +} + +async fn setup() -> (CdpContext, String) { + std::env::set_var("OBSCURA_ALLOW_PRIVATE_NETWORK", "1"); + let mut ctx = CdpContext::new(); + let page_id = ctx.create_page(); + let session_id = "treewalker-session"; + ctx.sessions.insert(session_id.to_string(), page_id); + cdp( + &mut ctx, + 1, + "Page.navigate", + json!({"url": serve_once().await, "waitUntil": "load"}), + session_id, + ) + .await; + (ctx, session_id.to_string()) +} + +async fn eval(ctx: &mut CdpContext, session_id: &str, expression: &str) -> Value { + cdp( + ctx, + 2, + "Runtime.evaluate", + json!({"expression": expression, "returnByValue": true}), + session_id, + ) + .await +} + +#[tokio::test(flavor = "current_thread")] +async fn next_node_walks_the_whole_subtree_in_document_order() { + let (mut ctx, session_id) = setup().await; + let value = eval( + &mut ctx, + &session_id, + r#"(() => { + document.body.innerHTML = + ''; + const walker = document.createTreeWalker( + document.getElementById('r'), NodeFilter.SHOW_ELEMENT); + const seen = []; + let node; + while ((node = walker.nextNode())) seen.push(node.tagName); + return JSON.stringify(seen); + })()"#, + ) + .await; + let seen: Vecone
two
three