Skip to content

Commit 3f36350

Browse files
committed
wip: add a screenshot test for svg rendering
1 parent 4ca16f9 commit 3f36350

File tree

3 files changed

+250
-1
lines changed

3 files changed

+250
-1
lines changed

packages/yew/Cargo.toml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,28 @@ trybuild = "1"
9292

9393
[dev-dependencies.web-sys]
9494
version = "0.3"
95-
features = ["ShadowRootInit", "ShadowRootMode", "HtmlButtonElement"]
95+
features = [
96+
"ShadowRootInit",
97+
"ShadowRootMode",
98+
"HtmlButtonElement",
99+
"HtmlCanvasElement",
100+
"CanvasRenderingContext2d",
101+
"HtmlVideoElement",
102+
"MediaStream",
103+
"MediaStreamTrack",
104+
"MediaDevices",
105+
"Navigator",
106+
"DisplayMediaStreamConstraints",
107+
"MediaTrackSettings",
108+
"DomRect",
109+
"ImageData",
110+
"Blob",
111+
"BlobPropertyBag",
112+
"Url",
113+
"HtmlImageElement",
114+
"CssStyleDeclaration",
115+
"Element",
116+
]
96117

97118
[features]
98119
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]

packages/yew/tests/svg_render.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
2+
3+
use wasm_bindgen::prelude::*;
4+
use wasm_bindgen_test::*;
5+
use web_sys::wasm_bindgen::JsCast;
6+
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlVideoElement};
7+
use yew::prelude::*;
8+
9+
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
10+
11+
#[function_component]
12+
fn SvgWithDropShadow() -> Html {
13+
html! {
14+
<svg id="test-svg">
15+
<defs>
16+
<filter id="glow">
17+
<feDropShadow dx="0" dy="0" stdDeviation="10" flood-color="red"/>
18+
</filter>
19+
</defs>
20+
<rect width="100" height="100" filter="url(#glow)" />
21+
</svg>
22+
}
23+
}
24+
25+
#[wasm_bindgen_test]
26+
async fn svg_camel_case_elements_render() {
27+
let window = web_sys::window().unwrap();
28+
let document = window.document().unwrap();
29+
let body = document.body().unwrap();
30+
31+
// Create container
32+
let container = document.create_element("div").unwrap();
33+
container.set_id("test-container");
34+
body.append_child(&container).unwrap();
35+
36+
// Mount component
37+
yew::Renderer::<SvgWithDropShadow>::with_root(container.clone().unchecked_into()).render();
38+
39+
// Wait for render to complete
40+
yew::platform::time::sleep(std::time::Duration::from_millis(1000)).await;
41+
42+
// Get the SVG element to verify it exists
43+
let svg_element = document
44+
.get_element_by_id("test-svg")
45+
.expect("SVG element should exist");
46+
47+
// Get SVG bounds for pixel analysis
48+
let rect = svg_element.get_bounding_client_rect();
49+
50+
// Use getDisplayMedia to capture the screen
51+
let navigator = window.navigator();
52+
let media_devices = navigator.media_devices().unwrap();
53+
54+
// Create options for getDisplayMedia
55+
let constraints = web_sys::DisplayMediaStreamConstraints::new();
56+
// Set preferCurrentTab via reflection (Chrome-only feature)
57+
let _ = js_sys::Reflect::set(
58+
&constraints,
59+
&JsValue::from("preferCurrentTab"),
60+
&JsValue::from(true),
61+
);
62+
// Set video with frameRate
63+
let video_constraints = js_sys::Object::new();
64+
let _ = js_sys::Reflect::set(
65+
&video_constraints,
66+
&JsValue::from("frameRate"),
67+
&JsValue::from(30),
68+
);
69+
constraints.set_video(&video_constraints);
70+
71+
// Try to get the display media stream
72+
let stream_promise = media_devices
73+
.get_display_media_with_constraints(&constraints)
74+
.unwrap();
75+
let stream_result = wasm_bindgen_futures::JsFuture::from(stream_promise).await;
76+
77+
// Check if getDisplayMedia failed (likely Firefox without user interaction)
78+
let stream = match stream_result {
79+
Ok(stream) => stream.dyn_into::<web_sys::MediaStream>().unwrap(),
80+
Err(_) => {
81+
// We are likely in Firefox, there is no way of granting permission
82+
// for screen capture without user interaction in automated tests
83+
web_sys::console::log_1(
84+
&"getDisplayMedia failed - likely Firefox, skipping pixel test".into(),
85+
);
86+
return;
87+
}
88+
};
89+
90+
// Create a video element to capture frames
91+
let video = document
92+
.create_element("video")
93+
.unwrap()
94+
.dyn_into::<HtmlVideoElement>()
95+
.unwrap();
96+
video.set_autoplay(true);
97+
video.set_muted(true);
98+
js_sys::Reflect::set(
99+
&video,
100+
&JsValue::from("playsInline"),
101+
&JsValue::from(true),
102+
);
103+
video.set_src_object(Some(&stream));
104+
105+
// Add video to DOM (invisible but positioned at 0,0)
106+
let style = video.dyn_ref::<web_sys::HtmlElement>().unwrap().style();
107+
style.set_property("position", "fixed").unwrap();
108+
style.set_property("top", "0").unwrap();
109+
style.set_property("left", "0").unwrap();
110+
style.set_property("pointer-events", "none").unwrap();
111+
style.set_property("visibility", "hidden").unwrap();
112+
body.append_child(&video).unwrap();
113+
114+
// Wait for video to start playing
115+
let play_promise = video.play().unwrap();
116+
wasm_bindgen_futures::JsFuture::from(play_promise)
117+
.await
118+
.ok();
119+
120+
// Wait a bit for the video feed to stabilize
121+
yew::platform::time::sleep(std::time::Duration::from_millis(500)).await;
122+
123+
// Get video track settings to know dimensions
124+
let tracks = stream.get_video_tracks();
125+
let track = tracks
126+
.get(0)
127+
.dyn_into::<web_sys::MediaStreamTrack>()
128+
.unwrap();
129+
let settings = track.get_settings();
130+
131+
let width = settings.get_width().unwrap_or(1920) as u32;
132+
let height = settings.get_height().unwrap_or(1080) as u32;
133+
134+
// Create canvas and draw the video frame
135+
let canvas = document
136+
.create_element("canvas")
137+
.unwrap()
138+
.dyn_into::<HtmlCanvasElement>()
139+
.unwrap();
140+
canvas.set_width(width);
141+
canvas.set_height(height);
142+
143+
let ctx = canvas
144+
.get_context("2d")
145+
.unwrap()
146+
.unwrap()
147+
.dyn_into::<CanvasRenderingContext2d>()
148+
.unwrap();
149+
150+
// Draw the video frame to canvas
151+
ctx.draw_image_with_html_video_element(&video, 0.0, 0.0)
152+
.unwrap();
153+
154+
// Stop the capture
155+
let tracks = stream.get_tracks();
156+
157+
// Get image data for pixel analysis
158+
let image_data = ctx
159+
.get_image_data(0.0, 0.0, width as f64, height as f64)
160+
.unwrap();
161+
let data = image_data.data();
162+
163+
// Analyze pixels around the center where the rect should be
164+
let center_x = (rect.left() + rect.width() / 2.0) as i32;
165+
let center_y = (rect.top() + rect.height() / 2.0) as i32;
166+
167+
let mut has_non_white_pixels = false;
168+
let mut sample_pixels = Vec::new();
169+
170+
// Check a grid of pixels around the center
171+
for dy in (-60..=60).step_by(10) {
172+
for dx in (-60..=60).step_by(10) {
173+
let x = center_x + dx;
174+
let y = center_y + dy;
175+
176+
if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
177+
let idx = ((y * width as i32 + x) * 4) as usize;
178+
let r = data[idx];
179+
let g = data[idx + 1];
180+
let b = data[idx + 2];
181+
182+
// Log some sample pixels for debugging
183+
if sample_pixels.len() < 10 {
184+
sample_pixels.push(format!("({},{}): rgb({},{},{})", x, y, r, g, b));
185+
}
186+
187+
// Check if pixel is not white (with tolerance)
188+
if r < 250 || g < 250 || b < 250 {
189+
has_non_white_pixels = true;
190+
}
191+
}
192+
}
193+
}
194+
195+
// Convert canvas to base64 for inspection
196+
let data_url = canvas.to_data_url().unwrap();
197+
198+
// Log pixel analysis
199+
web_sys::console::log_1(&format!("Has non-white pixels: {}", has_non_white_pixels).into());
200+
web_sys::console::log_1(&format!("Sample pixels: {:?}", sample_pixels).into());
201+
web_sys::console::log_1(&format!("Screenshot data URL (copy and paste in browser): {}", data_url).into());
202+
203+
// Also log the SVG bounds and center position
204+
web_sys::console::log_1(&format!("SVG bounds: left={}, top={}, width={}, height={}",
205+
rect.left(), rect.top(), rect.width(), rect.height()).into());
206+
web_sys::console::log_1(&format!("Center position: ({}, {})", center_x, center_y).into());
207+
web_sys::console::log_1(&format!("Canvas size: {}x{}", width, height).into());
208+
209+
assert!(has_non_white_pixels, "Expected the rect to render");
210+
211+
for i in 0..tracks.length() {
212+
let track = tracks.get(i);
213+
if !track.is_undefined() {
214+
track
215+
.dyn_into::<web_sys::MediaStreamTrack>()
216+
.unwrap()
217+
.stop();
218+
}
219+
}
220+
}

packages/yew/webdriver.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"goog:chromeOptions": {
3+
"args": [
4+
"--use-fake-ui-for-media-stream",
5+
"--use-fake-device-for-media-stream"
6+
]
7+
}
8+
}

0 commit comments

Comments
 (0)