diff --git a/Cargo.lock b/Cargo.lock index ae4f4b8..a1b0f5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1370,6 +1370,7 @@ version = "0.1.0" dependencies = [ "axum", "futures-util", + "scraper", "slhx-core", "slhx-js", "tokio", diff --git a/slhx-axum/Cargo.toml b/slhx-axum/Cargo.toml index 81514b0..9c09745 100644 --- a/slhx-axum/Cargo.toml +++ b/slhx-axum/Cargo.toml @@ -13,4 +13,5 @@ slhx-core = { path = "../slhx-core" } slhx-js = { path = "../slhx-js" } [dev-dependencies] +scraper = "0.23" tokio = { version = "1", features = ["macros", "rt"] } diff --git a/slhx-axum/src/lib.rs b/slhx-axum/src/lib.rs index 075c18d..a62297c 100644 --- a/slhx-axum/src/lib.rs +++ b/slhx-axum/src/lib.rs @@ -533,30 +533,46 @@ mod tests { use super::{encode_sse_batch, html_with_root_fingerprint, sse, BuildFingerprint, InteractionForm, SLHX_SSE_EVENT}; use axum::{body::{to_bytes, Body}, extract::FromRequest, http::{header, Request}, response::IntoResponse}; use futures_util::stream; + use scraper::{Html, Selector}; use slhx_core::{EffectBatch, EFFECT_BATCH_ABI_VERSION}; use std::convert::Infallible; + fn selector(value: &str) -> Selector { + Selector::parse(value).expect("test selector parses") + } + #[test] fn root_fingerprint_is_added_to_initial_root() { + // req: test/005 let html = html_with_root_fingerprint( "
Docs
".into(), BuildFingerprint(99), ); - assert_eq!( - html, - "
Docs
" - ); + let document = Html::parse_document(&html); + let root = document + .select(&selector("main[data-slhx-root]")) + .next() + .expect("root element is rendered"); + assert_eq!(root.value().attr("data-slhx-fp"), Some("99")); + assert_eq!(root.text().collect::(), "Docs"); } #[test] fn existing_root_fingerprint_is_preserved() { + // req: test/005 let html = html_with_root_fingerprint( "
Docs
".into(), BuildFingerprint(99), ); - assert_eq!(html, "
Docs
"); + let document = Html::parse_fragment(&html); + let root = document + .select(&selector("main[data-slhx-root]")) + .next() + .expect("root element is rendered"); + assert_eq!(root.value().attr("data-slhx-fp"), Some("1")); + assert_eq!(root.text().collect::(), "Docs"); } #[tokio::test] diff --git a/slhx-axum/tests/response.rs b/slhx-axum/tests/response.rs index afd8717..ee7fbe1 100644 --- a/slhx-axum/tests/response.rs +++ b/slhx-axum/tests/response.rs @@ -1,5 +1,6 @@ use axum::http::{header, HeaderMap}; use axum::response::IntoResponse; +use scraper::{Html, Selector}; use slhx_axum::{ runtime_js, DispatchRejection, EffectResponse, HandlerRegistry, InteractionForm, InteractionFormRejection, PageMode, PageRequest, PageResponse, SLHX_CONTENT_TYPE, @@ -7,6 +8,10 @@ use slhx_axum::{ }; use slhx_core::{push, BuildFingerprint, Slot}; +fn selector(value: &str) -> Selector { + Selector::parse(value).expect("test selector parses") +} + #[test] fn page_mode_detects_partial_header() { let mut headers = HeaderMap::new(); @@ -18,17 +23,28 @@ fn page_mode_detects_partial_header() { #[test] fn page_request_wraps_full_pages_and_leaves_partials_unwrapped() { - let full = PageRequest { mode: PageMode::Full }.page("
Docs
", |content| { - format!("{content}") - }); + // req: test/005 + let full = PageRequest { mode: PageMode::Full }.page( + "
Docs
", + |content| format!("{content}"), + ); assert_eq!(full.mode, PageMode::Full); - assert_eq!(full.html, "
Docs
"); + let full_document = Html::parse_document(&full.html); + assert_eq!( + full_document + .select(&selector("body[data-shell=\"docs\"] main[data-page=\"docs\"]")) + .count(), + 1 + ); - let partial = PageRequest { mode: PageMode::Partial }.page("
Docs
", |content| { - format!("{content}") - }); + let partial = PageRequest { mode: PageMode::Partial }.page( + "
Docs
", + |content| format!("{content}"), + ); assert_eq!(partial.mode, PageMode::Partial); - assert_eq!(partial.html, "
Docs
"); + let partial_fragment = Html::parse_fragment(&partial.html); + assert_eq!(partial_fragment.select(&selector("main[data-page=\"docs\"]")).count(), 1); + assert_eq!(partial_fragment.select(&selector("body[data-shell=\"docs\"]")).count(), 0); } #[test]