diff --git a/crates/botloftd/src/browser/reader.js b/crates/botloftd/src/browser/reader.js index b489f579..46f59c42 100644 --- a/crates/botloftd/src/browser/reader.js +++ b/crates/botloftd/src/browser/reader.js @@ -165,8 +165,9 @@ const walk = (node, parentCursor, depth) => { if (++nodes > MAX_NODES || depth > MAX_DEPTH) return; if (node.nodeType === Node.TEXT_NODE) { - const text = node.nodeValue.replace(/\s+/g, " "); - if (text.trim()) out.push(text); + // A text node of spaces alone still parts words: pages that put + // each letter in its own element keep their spaces that way. + out.push(node.nodeValue.replace(/\s+/g, " ")); return; } if (node.nodeType !== Node.ELEMENT_NODE) return; diff --git a/crates/botloftd/tests/browser.rs b/crates/botloftd/tests/browser.rs index 9b69d4e4..a04195ca 100644 --- a/crates/botloftd/tests/browser.rs +++ b/crates/botloftd/tests/browser.rs @@ -89,6 +89,19 @@ async fn a_bot_fills_a_form_after_the_owner_allows_the_site() { assert!(closed.expect_err("closed").contains("not open")); } +#[tokio::test(flavor = "multi_thread")] +async fn words_made_of_one_element_per_letter_keep_their_spaces() { + let Some(mut b) = setup().await else { return }; + let opening = call( + &b.mcp, + "browser_open", + json!({ "url": format!("{}/letters", b.site) }), + ); + answer_site(&mut b.app, true, None).await; + let page = opening.await.expect("task").expect("page"); + assert!(page.contains("Two words"), "{page}"); +} + #[tokio::test(flavor = "multi_thread")] async fn a_site_the_owner_denies_stays_closed() { let Some(mut b) = setup().await else { return }; diff --git a/crates/botloftd/tests/common/site.rs b/crates/botloftd/tests/common/site.rs index bca71719..d663d1bf 100644 --- a/crates/botloftd/tests/common/site.rs +++ b/crates/botloftd/tests/common/site.rs @@ -1,8 +1,9 @@ //! A small web site on 127.0.0.1 for the browser tests: a form, the page //! it leads to, a link that opens a new tab, a button that shows a dialog, //! a sign-in page with its fields at fixed points, for the owner's hands, -//! a page that says how big its window is, one that counts its loads and -//! one that counts the times it went out of sight. +//! a page that says how big its window is, one that counts its loads, one +//! that counts the times it went out of sight and one with each letter in +//! its own element. use std::collections::HashMap; use std::net::SocketAddr; @@ -25,6 +26,11 @@ const POPUP: &str = "Links\ Open in a new tab\ "; +/// Each letter in a span, spaces too, as example.com's own script does. +const LETTERS: &str = "Letters

Tw\ + o word\ + s

"; + /// The fields' middles: user (200, 115), password (200, 215). const LOGIN: &str = "Sign in