Skip to content

Commit af5e57d

Browse files
authored
Implement ElementHandle.ScreenshotStreamAsync (#211)
1 parent aa63c27 commit af5e57d

File tree

14 files changed

+406
-106
lines changed

14 files changed

+406
-106
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Xunit;
4+
5+
namespace PuppeteerSharp.Tests.ElementHandleTests
6+
{
7+
[Collection("PuppeteerLoaderFixture collection")]
8+
public class ScreenshotTests : PuppeteerPageBaseTest
9+
{
10+
[Fact]
11+
public async Task ShouldFire()
12+
{
13+
await Page.SetViewportAsync(new ViewPortOptions
14+
{
15+
Width = 500,
16+
Height = 500
17+
});
18+
await Page.GoToAsync(TestConstants.ServerUrl + "/grid.html");
19+
await Page.EvaluateExpressionAsync("window.scrollBy(50, 100)");
20+
var elementHandle = await Page.QuerySelectorAsync(".box:nth-of-type(3)");
21+
var screenshot = await elementHandle.ScreenshotStreamAsync();
22+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-bounding-box.png", screenshot));
23+
}
24+
25+
[Fact]
26+
public async Task ShouldTakeIntoAccountPaddingAndBorder()
27+
{
28+
await Page.SetViewportAsync(new ViewPortOptions
29+
{
30+
Width = 500,
31+
Height = 500
32+
});
33+
await Page.SetContentAsync(@"
34+
something above
35+
<style> div {
36+
border: 2px solid blue;
37+
background: green;
38+
width: 50px;
39+
height: 50px;
40+
}
41+
</style>
42+
<div></div>
43+
");
44+
var elementHandle = await Page.QuerySelectorAsync("div");
45+
var screenshot = await elementHandle.ScreenshotStreamAsync();
46+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-padding-border.png", screenshot));
47+
}
48+
49+
[Fact]
50+
public async Task ShouldScrollElementIntoView()
51+
{
52+
await Page.SetViewportAsync(new ViewPortOptions
53+
{
54+
Width = 500,
55+
Height = 500
56+
});
57+
await Page.SetContentAsync(@"
58+
something above
59+
<style> div.above {
60+
border: 2px solid blue;
61+
background: red;
62+
height: 1500px;
63+
}
64+
div.to-screenshot {
65+
border: 2px solid blue;
66+
background: green;
67+
width: 50px;
68+
height: 50px;
69+
}
70+
</style>
71+
<div class='above'></div>
72+
<div class='to-screenshot'></div>
73+
");
74+
var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot");
75+
var screenshot = await elementHandle.ScreenshotStreamAsync();
76+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-scrolled-into-view.png", screenshot));
77+
}
78+
79+
[Fact]
80+
public async Task ShouldWorkWithARotatedElement()
81+
{
82+
await Page.SetViewportAsync(new ViewPortOptions
83+
{
84+
Width = 500,
85+
Height = 500
86+
});
87+
await Page.SetContentAsync(@"
88+
<div style='position: absolute;
89+
top: 100px;
90+
left: 100px;
91+
width: 100px;
92+
height: 100px;
93+
background: green;
94+
transform: rotateZ(200deg); '>&nbsp;</div>
95+
");
96+
var elementHandle = await Page.QuerySelectorAsync("div");
97+
var screenshot = await elementHandle.ScreenshotStreamAsync();
98+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-rotate.png", screenshot));
99+
}
100+
101+
[Fact]
102+
public async Task ShouldFailToScreenshotADetachedElement()
103+
{
104+
await Page.SetContentAsync("<h1>remove this</h1>");
105+
var elementHandle = await Page.QuerySelectorAsync("h1");
106+
await Page.EvaluateFunctionAsync("element => element.remove()", elementHandle);
107+
108+
var exception = await Assert.ThrowsAsync<PuppeteerException>(elementHandle.ScreenshotStreamAsync);
109+
Assert.Equal("Node is detached from document", exception.Message);
110+
}
111+
}
112+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Newtonsoft.Json;
3+
4+
namespace PuppeteerSharp.Tests.Input
5+
{
6+
public class Dimensions
7+
{
8+
[JsonProperty("x")]
9+
public decimal X { get; set; }
10+
[JsonProperty("y")]
11+
public decimal Y { get; set; }
12+
[JsonProperty("width")]
13+
public decimal Width { get; set; }
14+
[JsonProperty("height")]
15+
public decimal Height { get; set; }
16+
}
17+
}

lib/PuppeteerSharp.Tests/Input/InputTests.cs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public async Task ShouldFailToClickAMissingButton()
7777
[Fact]
7878
public async Task ShouldNotHangWithTouchEnabledViewports()
7979
{
80-
await Page.SetViewport(TestConstants.IPhone.ViewPort);
80+
await Page.SetViewportAsync(TestConstants.IPhone.ViewPort);
8181
await Page.Mouse.DownAsync();
8282
await Page.Mouse.MoveAsync(100, 10);
8383
await Page.Mouse.UpAsync();
@@ -488,7 +488,7 @@ public async Task ShouldClickTheButtonInsideAnIframe()
488488
[Fact]
489489
public async Task ShouldClickTheButtonWithDeviceScaleFactorSet()
490490
{
491-
await Page.SetViewport(new ViewPortOptions { Width = 400, Height = 400, DeviceScaleFactor = 5 });
491+
await Page.SetViewportAsync(new ViewPortOptions { Width = 400, Height = 400, DeviceScaleFactor = 5 });
492492
Assert.Equal(5, await Page.EvaluateExpressionAsync<int>("window.devicePixelRatio"));
493493
await Page.SetContentAsync("<div style=\"width:100px;height:100px\">spacer</div>");
494494
await FrameUtils.AttachFrameAsync(Page, "button-test", TestConstants.ServerUrl + "/input/button.html");
@@ -540,16 +540,4 @@ public async Task ShouldThrowOnUnknownKeys()
540540
await Assert.ThrowsAsync<KeyNotFoundException>(() => Page.Keyboard.PressAsync("😊"));
541541
}
542542
}
543-
544-
class Dimensions
545-
{
546-
[JsonProperty("x")]
547-
public decimal X { get; set; }
548-
[JsonProperty("y")]
549-
public decimal Y { get; set; }
550-
[JsonProperty("width")]
551-
public decimal Width { get; set; }
552-
[JsonProperty("height")]
553-
public decimal Height { get; set; }
554-
}
555543
}

lib/PuppeteerSharp.Tests/Page/ScreenshotTests.cs

Lines changed: 16 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public async Task ShouldWorkWithFile()
1919

2020
using (var page = await Browser.NewPageAsync())
2121
{
22-
await page.SetViewport(new ViewPortOptions
22+
await page.SetViewportAsync(new ViewPortOptions
2323
{
2424
Width = 500,
2525
Height = 500
@@ -36,7 +36,7 @@ await page.SetViewport(new ViewPortOptions
3636

3737
fileInfo = new FileInfo(outputFile);
3838
Assert.True(new FileInfo(outputFile).Length > 0);
39-
Assert.True(PixelMatch("screenshot-sanity.png", outputFile));
39+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", outputFile));
4040

4141
if (fileInfo.Exists)
4242
{
@@ -50,14 +50,14 @@ public async Task ShouldWork()
5050
{
5151
using (var page = await Browser.NewPageAsync())
5252
{
53-
await page.SetViewport(new ViewPortOptions
53+
await page.SetViewportAsync(new ViewPortOptions
5454
{
5555
Width = 500,
5656
Height = 500
5757
});
5858
await page.GoToAsync(TestConstants.ServerUrl + "/grid.html");
5959
var screenshot = await page.ScreenshotStreamAsync();
60-
Assert.True(PixelMatch("screenshot-sanity.png", screenshot));
60+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", screenshot));
6161
}
6262
}
6363

@@ -66,7 +66,7 @@ public async Task ShouldClipRect()
6666
{
6767
using (var page = await Browser.NewPageAsync())
6868
{
69-
await page.SetViewport(new ViewPortOptions
69+
await page.SetViewportAsync(new ViewPortOptions
7070
{
7171
Width = 500,
7272
Height = 500
@@ -82,7 +82,7 @@ await page.SetViewport(new ViewPortOptions
8282
Height = 100
8383
}
8484
});
85-
Assert.True(PixelMatch("screenshot-clip-rect.png", screenshot));
85+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-rect.png", screenshot));
8686
}
8787
}
8888

@@ -91,7 +91,7 @@ public async Task ShouldWorkForOffscreenClip()
9191
{
9292
using (var page = await Browser.NewPageAsync())
9393
{
94-
await page.SetViewport(new ViewPortOptions
94+
await page.SetViewportAsync(new ViewPortOptions
9595
{
9696
Width = 500,
9797
Height = 500
@@ -107,7 +107,7 @@ await page.SetViewport(new ViewPortOptions
107107
Height = 100
108108
}
109109
});
110-
Assert.True(PixelMatch("screenshot-offscreen-clip.png", screenshot));
110+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-offscreen-clip.png", screenshot));
111111
}
112112
}
113113

@@ -116,7 +116,7 @@ public async Task ShouldRunInParallel()
116116
{
117117
using (var page = await Browser.NewPageAsync())
118118
{
119-
await page.SetViewport(new ViewPortOptions
119+
await page.SetViewportAsync(new ViewPortOptions
120120
{
121121
Width = 500,
122122
Height = 500
@@ -139,7 +139,7 @@ await page.SetViewport(new ViewPortOptions
139139
}
140140

141141
await Task.WhenAll(tasks);
142-
Assert.True(PixelMatch("grid-cell-1.png", tasks[0].Result));
142+
Assert.True(ScreenshotHelper.PixelMatch("grid-cell-1.png", tasks[0].Result));
143143
}
144144
}
145145

@@ -148,7 +148,7 @@ public async Task ShouldTakeFullPageScreenshots()
148148
{
149149
using (var page = await Browser.NewPageAsync())
150150
{
151-
await page.SetViewport(new ViewPortOptions
151+
await page.SetViewportAsync(new ViewPortOptions
152152
{
153153
Width = 500,
154154
Height = 500
@@ -158,7 +158,7 @@ await page.SetViewport(new ViewPortOptions
158158
{
159159
FullPage = true
160160
});
161-
Assert.True(PixelMatch("screenshot-grid-fullpage.png", screenshot));
161+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-grid-fullpage.png", screenshot));
162162
}
163163
}
164164

@@ -200,7 +200,7 @@ public async Task ShouldRunInParallelInMultiplePages()
200200

201201
for (var i = 0; i < n; i++)
202202
{
203-
Assert.True(PixelMatch($"grid-cell-{i}.png", screenshotTasks[i].Result));
203+
Assert.True(ScreenshotHelper.PixelMatch($"grid-cell-{i}.png", screenshotTasks[i].Result));
204204
}
205205

206206
var closeTasks = new List<Task>();
@@ -217,7 +217,7 @@ public async Task ShouldAllowTransparency()
217217
{
218218
using (var page = await Browser.NewPageAsync())
219219
{
220-
await page.SetViewport(new ViewPortOptions
220+
await page.SetViewportAsync(new ViewPortOptions
221221
{
222222
Width = 100,
223223
Height = 100
@@ -228,7 +228,7 @@ await page.SetViewport(new ViewPortOptions
228228
OmitBackground = true
229229
});
230230

231-
Assert.True(PixelMatch("transparent.png", screenshot));
231+
Assert.True(ScreenshotHelper.PixelMatch("transparent.png", screenshot));
232232
}
233233
}
234234

@@ -248,58 +248,8 @@ public async Task ShouldWorkWithOddClipSizeOnRetinaDisplays()
248248
}
249249
});
250250

251-
Assert.True(PixelMatch("screenshot-clip-odd-size.png", screenshot));
251+
Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-odd-size.png", screenshot));
252252
}
253253
}
254-
255-
private bool PixelMatch(string screenShotFile, string fileName)
256-
{
257-
using (Stream stream = File.Open(fileName, FileMode.Open))
258-
{
259-
return PixelMatch(screenShotFile, stream);
260-
}
261-
}
262-
263-
private bool PixelMatch(string screenShotFile, Stream screenshot)
264-
{
265-
const int pixelThreshold = 10;
266-
const decimal totalTolerance = 0.05m;
267-
268-
var baseImage = Image.Load(Path.Combine(TestUtils.FindParentDirectory("Screenshots"), screenShotFile));
269-
var compareImage = Image.Load(screenshot);
270-
271-
//Just for debugging purpose
272-
compareImage.Save(Path.Combine(TestUtils.FindParentDirectory("Screenshots"), "test.png"));
273-
274-
if (baseImage.Width != compareImage.Width || baseImage.Height != compareImage.Height)
275-
{
276-
return false;
277-
}
278-
279-
var rgb1 = default(Rgb24);
280-
var rgb2 = default(Rgb24);
281-
var invalidPixelsCount = 0;
282-
283-
for (int y = 0; y < baseImage.Height; y++)
284-
{
285-
for (int x = 0; x < baseImage.Width; x++)
286-
{
287-
var pixelA = baseImage[x, y];
288-
var pixelB = compareImage[x, y];
289-
290-
pixelA.ToRgb24(ref rgb1);
291-
pixelB.ToRgb24(ref rgb2);
292-
293-
if (Math.Abs(rgb1.R - rgb2.R) > pixelThreshold ||
294-
Math.Abs(rgb1.G - rgb2.G) > pixelThreshold ||
295-
Math.Abs(rgb1.B - rgb2.B) > pixelThreshold)
296-
{
297-
invalidPixelsCount++;
298-
}
299-
}
300-
}
301-
302-
return (invalidPixelsCount / (baseImage.Height * baseImage.Width)) < totalTolerance;
303-
}
304254
}
305255
}

0 commit comments

Comments
 (0)