diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml
index f90bd43376e..8ad733d44d2 100644
--- a/packages/yew/Cargo.toml
+++ b/packages/yew/Cargo.toml
@@ -92,7 +92,28 @@ trybuild = "1"
[dev-dependencies.web-sys]
version = "0.3"
-features = ["ShadowRootInit", "ShadowRootMode", "HtmlButtonElement"]
+features = [
+ "ShadowRootInit",
+ "ShadowRootMode",
+ "HtmlButtonElement",
+ "HtmlCanvasElement",
+ "CanvasRenderingContext2d",
+ "HtmlVideoElement",
+ "MediaStream",
+ "MediaStreamTrack",
+ "MediaDevices",
+ "Navigator",
+ "DisplayMediaStreamConstraints",
+ "MediaTrackSettings",
+ "DomRect",
+ "ImageData",
+ "Blob",
+ "BlobPropertyBag",
+ "Url",
+ "HtmlImageElement",
+ "CssStyleDeclaration",
+ "Element",
+]
[features]
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]
diff --git a/packages/yew/tests/svg_render.rs b/packages/yew/tests/svg_render.rs
new file mode 100644
index 00000000000..1c4ffb412f5
--- /dev/null
+++ b/packages/yew/tests/svg_render.rs
@@ -0,0 +1,230 @@
+#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
+
+use wasm_bindgen::prelude::*;
+use wasm_bindgen_test::*;
+use web_sys::wasm_bindgen::JsCast;
+use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlVideoElement};
+use yew::prelude::*;
+
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+#[function_component]
+fn SvgWithDropShadow() -> Html {
+ html! {
+
+ }
+}
+
+#[wasm_bindgen_test]
+async fn svg_camel_case_elements_render() {
+ let window = web_sys::window().unwrap();
+ let document = window.document().unwrap();
+ let body = document.body().unwrap();
+
+ // Create container
+ let container = document.create_element("div").unwrap();
+ container.set_id("test-container");
+ body.append_child(&container).unwrap();
+
+ // Mount component
+ yew::Renderer::::with_root(container.clone().unchecked_into()).render();
+
+ // Wait for render to complete
+ yew::platform::time::sleep(std::time::Duration::from_millis(1000)).await;
+
+ // Get the SVG element to verify it exists
+ let svg_element = document
+ .get_element_by_id("test-svg")
+ .expect("SVG element should exist");
+
+ // Get SVG bounds for pixel analysis
+ let rect = svg_element.get_bounding_client_rect();
+
+ // Use getDisplayMedia to capture the screen
+ let navigator = window.navigator();
+ let media_devices = navigator.media_devices().unwrap();
+
+ // Create options for getDisplayMedia
+ let constraints = web_sys::DisplayMediaStreamConstraints::new();
+ // Set preferCurrentTab via reflection (Chrome-only feature)
+ let _ = js_sys::Reflect::set(
+ &constraints,
+ &JsValue::from("preferCurrentTab"),
+ &JsValue::from(true),
+ );
+ // Set video with frameRate
+ let video_constraints = js_sys::Object::new();
+ let _ = js_sys::Reflect::set(
+ &video_constraints,
+ &JsValue::from("frameRate"),
+ &JsValue::from(30),
+ );
+ constraints.set_video(&video_constraints);
+
+ // Try to get the display media stream
+ let stream_promise = media_devices
+ .get_display_media_with_constraints(&constraints)
+ .unwrap();
+ let stream_result = wasm_bindgen_futures::JsFuture::from(stream_promise).await;
+
+ // Check if getDisplayMedia failed (likely Firefox without user interaction)
+ let stream = match stream_result {
+ Ok(stream) => stream.dyn_into::().unwrap(),
+ Err(_) => {
+ // We are likely in Firefox, there is no way of granting permission
+ // for screen capture without user interaction in automated tests
+ web_sys::console::log_1(
+ &"getDisplayMedia failed - likely Firefox, skipping pixel test".into(),
+ );
+ return;
+ }
+ };
+
+ // Create a video element to capture frames
+ let video = document
+ .create_element("video")
+ .unwrap()
+ .dyn_into::()
+ .unwrap();
+ video.set_autoplay(true);
+ video.set_muted(true);
+ js_sys::Reflect::set(&video, &JsValue::from("playsInline"), &JsValue::from(true));
+ video.set_src_object(Some(&stream));
+
+ // Add video to DOM (invisible but positioned at 0,0)
+ let style = video.dyn_ref::().unwrap().style();
+ style.set_property("position", "fixed").unwrap();
+ style.set_property("top", "0").unwrap();
+ style.set_property("left", "0").unwrap();
+ style.set_property("pointer-events", "none").unwrap();
+ style.set_property("visibility", "hidden").unwrap();
+ body.append_child(&video).unwrap();
+
+ // Wait for video to start playing
+ let play_promise = video.play().unwrap();
+ wasm_bindgen_futures::JsFuture::from(play_promise)
+ .await
+ .ok();
+
+ // Wait a bit for the video feed to stabilize
+ yew::platform::time::sleep(std::time::Duration::from_millis(500)).await;
+
+ // Get video track settings to know dimensions
+ let tracks = stream.get_video_tracks();
+ let track = tracks
+ .get(0)
+ .dyn_into::()
+ .unwrap();
+ let settings = track.get_settings();
+
+ let width = settings.get_width().unwrap_or(1920) as u32;
+ let height = settings.get_height().unwrap_or(1080) as u32;
+
+ // Create canvas and draw the video frame
+ let canvas = document
+ .create_element("canvas")
+ .unwrap()
+ .dyn_into::()
+ .unwrap();
+ canvas.set_width(width);
+ canvas.set_height(height);
+
+ let ctx = canvas
+ .get_context("2d")
+ .unwrap()
+ .unwrap()
+ .dyn_into::()
+ .unwrap();
+
+ // Draw the video frame to canvas
+ ctx.draw_image_with_html_video_element(&video, 0.0, 0.0)
+ .unwrap();
+
+ // Stop the capture
+ let tracks = stream.get_tracks();
+
+ // Get image data for pixel analysis
+ let image_data = ctx
+ .get_image_data(0.0, 0.0, width as f64, height as f64)
+ .unwrap();
+ let data = image_data.data();
+
+ // Analyze pixels around the center where the rect should be
+ let center_x = (rect.left() + rect.width() / 2.0) as i32;
+ let center_y = (rect.top() + rect.height() / 2.0) as i32;
+
+ let mut has_non_white_pixels = false;
+ let mut sample_pixels = Vec::new();
+
+ // Check a grid of pixels around the center
+ for dy in (-60..=60).step_by(10) {
+ for dx in (-60..=60).step_by(10) {
+ let x = center_x + dx;
+ let y = center_y + dy;
+
+ if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
+ let idx = ((y * width as i32 + x) * 4) as usize;
+ let r = data[idx];
+ let g = data[idx + 1];
+ let b = data[idx + 2];
+
+ // Log some sample pixels for debugging
+ if sample_pixels.len() < 10 {
+ sample_pixels.push(format!("({},{}): rgb({},{},{})", x, y, r, g, b));
+ }
+
+ // Check if pixel is not white (with tolerance)
+ if r < 250 || g < 250 || b < 250 {
+ has_non_white_pixels = true;
+ }
+ }
+ }
+ }
+
+ // Convert canvas to base64 for inspection
+ let data_url = canvas.to_data_url().unwrap();
+
+ // Log pixel analysis
+ web_sys::console::log_1(&format!("Has non-white pixels: {}", has_non_white_pixels).into());
+ web_sys::console::log_1(&format!("Sample pixels: {:?}", sample_pixels).into());
+ web_sys::console::log_1(
+ &format!(
+ "Screenshot data URL (copy and paste in browser): {}",
+ data_url
+ )
+ .into(),
+ );
+
+ // Also log the SVG bounds and center position
+ web_sys::console::log_1(
+ &format!(
+ "SVG bounds: left={}, top={}, width={}, height={}",
+ rect.left(),
+ rect.top(),
+ rect.width(),
+ rect.height()
+ )
+ .into(),
+ );
+ web_sys::console::log_1(&format!("Center position: ({}, {})", center_x, center_y).into());
+ web_sys::console::log_1(&format!("Canvas size: {}x{}", width, height).into());
+
+ assert!(has_non_white_pixels, "Expected the rect to render");
+
+ for i in 0..tracks.length() {
+ let track = tracks.get(i);
+ if !track.is_undefined() {
+ track
+ .dyn_into::()
+ .unwrap()
+ .stop();
+ }
+ }
+}
diff --git a/packages/yew/webdriver.json b/packages/yew/webdriver.json
new file mode 100644
index 00000000000..7c63d37fcc1
--- /dev/null
+++ b/packages/yew/webdriver.json
@@ -0,0 +1,8 @@
+{
+ "goog:chromeOptions": {
+ "args": [
+ "--use-fake-ui-for-media-stream",
+ "--use-fake-device-for-media-stream"
+ ]
+ }
+}