Run Playwright-dotnet automation tests on LambdaTest cloud grid
Perform automation testing on 3000+ real desktop and mobile devices online.
/*
* MIT License
*
* Copyright (c) 2020 DarÃo Kondratiuk
* Modifications copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
namespace Microsoft.Playwright.Tests
{
///<playwright-file>page-screenshot.spec.ts</playwright-file>
public class PageScreenshotTests : PageTestEx
{
[PlaywrightTest("page-screenshot.spec.ts", "should work")]
public async Task ShouldWork()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
byte[] screenshot = await Page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should clip rect")]
public async Task ShouldClipRect()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
byte[] screenshot = await Page.ScreenshotAsync(new()
{
Clip = new()
{
X = 50,
Y = 100,
Width = 150,
Height = 100
}
}
);
Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-rect.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should clip rect with fullPage")]
public async Task ShouldClipRectWithFullPage()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
await Page.EvaluateAsync("() => window.scrollBy(150, 200)");
byte[] screenshot = await Page.ScreenshotAsync(new()
{
FullPage = true,
Clip = new()
{
X = 50,
Y = 100,
Width = 150,
Height = 100,
}
});
Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-rect.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should clip elements to the viewport")]
public async Task ShouldClipElementsToTheViewport()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
byte[] screenshot = await Page.ScreenshotAsync(new()
{
Clip = new()
{
X = 50,
Y = 450,
Width = 1000,
Height = 100,
}
});
Assert.True(ScreenshotHelper.PixelMatch("screenshot-offscreen-clip.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should throw on clip outside the viewport")]
public async Task ShouldThrowOnClipOutsideTheViewport()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
var exception = await PlaywrightAssert.ThrowsAsync<PlaywrightException>(() => Page.ScreenshotAsync(new()
{
Clip = new()
{
X = 50,
Y = 650,
Width = 100,
Height = 100,
}
}));
StringAssert.Contains("Clipped area is either empty or outside the resulting image", exception.Message);
}
[PlaywrightTest("page-screenshot.spec.ts", "should run in parallel")]
public async Task ShouldRunInParallel()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
var tasks = new List<Task<byte[]>>();
for (int i = 0; i < 3; ++i)
{
tasks.Add(Page.ScreenshotAsync(new()
{
Clip = new()
{
X = 50 * i,
Y = 0,
Width = 50,
Height = 50
}
}));
}
await TaskUtils.WhenAll(tasks);
Assert.True(ScreenshotHelper.PixelMatch("grid-cell-1.png", tasks[0].Result));
}
[PlaywrightTest("page-screenshot.spec.ts", "should take fullPage screenshots")]
public async Task ShouldTakeFullPageScreenshots()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
byte[] screenshot = await Page.ScreenshotAsync(new() { FullPage = true });
Assert.True(ScreenshotHelper.PixelMatch("screenshot-grid-fullpage.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should restore viewport after fullPage screenshot")]
public async Task ShouldRestoreViewportAfterFullPageScreenshot()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
await Page.ScreenshotAsync(new() { FullPage = true });
Assert.AreEqual(500, Page.ViewportSize.Width);
Assert.AreEqual(500, Page.ViewportSize.Height);
}
[PlaywrightTest("page-screenshot.spec.ts", "should run in parallel in multiple pages")]
public async Task ShouldRunInParallelInMultiplePages()
{
int n = 5;
var pageTasks = new List<Task<IPage>>();
for (int i = 0; i < n; i++)
{
async Task<IPage> Func()
{
var page = await Context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid.html");
return page;
}
pageTasks.Add(Func());
}
await TaskUtils.WhenAll(pageTasks);
var screenshotTasks = new List<Task<byte[]>>();
for (int i = 0; i < n; i++)
{
screenshotTasks.Add(pageTasks[i].Result.ScreenshotAsync(new()
{
Clip = new()
{
X = 50 * (i % 2),
Y = 0,
Width = 50,
Height = 50
}
}));
}
await TaskUtils.WhenAll(screenshotTasks);
for (int i = 0; i < n; i++)
{
Assert.True(ScreenshotHelper.PixelMatch($"grid-cell-{i % 2}.png", screenshotTasks[i].Result));
}
var closeTasks = new List<Task>();
for (int i = 0; i < n; i++)
{
closeTasks.Add(pageTasks[i].Result.CloseAsync());
}
await TaskUtils.WhenAll(closeTasks);
}
[PlaywrightTest("page-screenshot.spec.ts", "should allow transparency")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldAllowTransparency()
{
await Page.SetViewportSizeAsync(50, 150);
await Page.GotoAsync(Server.EmptyPage);
byte[] screenshot = await Page.ScreenshotAsync(new() { OmitBackground = true });
Assert.True(ScreenshotHelper.PixelMatch("transparent.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should render white background on jpeg file")]
public async Task ShouldRenderWhiteBackgroundOnJpegFile()
{
await Page.SetViewportSizeAsync(100, 100);
await Page.GotoAsync(Server.EmptyPage);
byte[] screenshot = await Page.ScreenshotAsync(new()
{
OmitBackground = true,
Type = ScreenshotType.Jpeg,
});
Assert.True(ScreenshotHelper.PixelMatch("white.jpg", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with odd clip size on Retina displays")]
public async Task ShouldWorkWithOddClipSizeOnRetinaDisplays()
{
byte[] screenshot = await Page.ScreenshotAsync(new()
{
Clip = new()
{
X = 0,
Y = 0,
Width = 11,
Height = 11
}
});
Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-odd-size.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWorkWithAMobileViewport()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
IsMobile = true,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/overflow.html");
byte[] screenshot = await page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-mobile.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport and clip")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWorkWithAMobileViewportAndClip()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
IsMobile = true,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/overflow.html");
byte[] screenshot = await page.ScreenshotAsync(new()
{
Clip = new()
{
X = 10,
Y = 10,
Width = 100,
Height = 150
}
});
Assert.True(ScreenshotHelper.PixelMatch("screenshot-mobile-clip.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport and fullPage")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWorkWithAMobileViewportAndFullPage()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
IsMobile = true,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/overflow-large.html");
byte[] screenshot = await page.ScreenshotAsync(new() { FullPage = true });
Assert.True(ScreenshotHelper.PixelMatch("screenshot-mobile-fullpage.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work for canvas")]
public async Task ShouldWorkForCanvas()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/screenshots/canvas.html");
byte[] screenshot = await Page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-canvas.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work for webgl")]
[Skip(SkipAttribute.Targets.Firefox, SkipAttribute.Targets.Webkit)]
public async Task ShouldWorkForWebgl()
{
await Page.SetViewportSizeAsync(640, 480);
await Page.GotoAsync(Server.Prefix + "/screenshots/webgl.html");
byte[] screenshot = await Page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-webgl.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work for translateZ")]
public async Task ShouldWorkForTranslateZ()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/screenshots/translateZ.html");
byte[] screenshot = await Page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-translateZ.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work while navigating")]
public async Task ShouldWorkWhileNavigating()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/redirectloop1.html");
for (int i = 0; i < 10; ++i)
{
try
{
await Page.ScreenshotAsync();
}
catch (Exception ex) when (ex.Message.Contains("Cannot take a screenshot while page is navigating"))
{
}
}
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with device scale factor")]
public async Task ShouldWorkWithDeviceScaleFactor()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
DeviceScaleFactor = 2,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid.html");
byte[] screenshot = await page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-device-scale-factor.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "should work with iframe in shadow")]
public async Task ShouldWorkWithiFrameInShadow()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 500,
Height = 500,
},
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid-iframe-in-shadow.html");
byte[] screenshot = await page.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-iframe.png", screenshot));
}
[PlaywrightTest("page-screenshot.spec.ts", "path option should work")]
public async Task PathOptionShouldWork()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
using var tmpDir = new TempDirectory();
string outputPath = Path.Combine(tmpDir.Path, "screenshot.png");
await Page.ScreenshotAsync(new() { Path = outputPath });
Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", outputPath));
}
[PlaywrightTest("page-screenshot.spec.ts", "path option should create subdirectories")]
public async Task PathOptionShouldCreateSubdirectories()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
using var tmpDir = new TempDirectory();
string outputPath = Path.Combine(tmpDir.Path, "these", "are", "directories", "screenshot.png");
await Page.ScreenshotAsync(new() { Path = outputPath });
Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", outputPath));
}
[PlaywrightTest("page-screenshot.spec.ts", "path option should detect joeg")]
public async Task PathOptionShouldDetectJpeg()
{
await Page.SetViewportSizeAsync(100, 100);
await Page.GotoAsync(Server.EmptyPage);
using var tmpDir = new TempDirectory();
string outputPath = Path.Combine(tmpDir.Path, "screenshot.jpg");
await Page.ScreenshotAsync(new() { Path = outputPath, OmitBackground = true });
Assert.True(ScreenshotHelper.PixelMatch("white.jpg", outputPath));
}
[PlaywrightTest("page-screenshot.spec.ts", "path option should throw for unsupported mime type")]
public async Task PathOptionShouldThrowForUnsupportedMimeType()
{
var exception = await PlaywrightAssert.ThrowsAsync<ArgumentException>(() => Page.ScreenshotAsync(new() { Path = "file.txt" }));
StringAssert.Contains("path: unsupported mime type \"text/plain\"", exception.Message);
}
}
}
/*
* MIT License
*
* Copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Playwright.NUnit;
using NUnit.Framework;
using SixLabors.ImageSharp;
namespace Microsoft.Playwright.Tests
{
///<playwright-file>elementhandle-screenshot.spec.ts</playwright-file>
public class ElementHandleScreenshotTests : PageTestEx
{
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work")]
public async Task ShouldWork()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
await Page.EvaluateAsync("window.scrollBy(50, 100)");
var elementHandle = await Page.QuerySelectorAsync(".box:nth-of-type(3)");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-bounding-box.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should take into account padding and border")]
public async Task ShouldTakeIntoAccountPaddingAndBorder()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>div {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div id=""d""></div>");
var elementHandle = await Page.QuerySelectorAsync("div#d");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-padding-border.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should capture full element when larger than viewport in parallel")]
public async Task ShouldCaptureFullElementWhenLargerThanViewportInParallel()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>
");
var elementHandles = await Page.QuerySelectorAllAsync("div.to-screenshot");
var screenshotTasks = elementHandles.Select(e => e.ScreenshotAsync()).ToArray();
await TaskUtils.WhenAll(screenshotTasks);
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-larger-than-viewport.png", screenshotTasks.ElementAt(2).Result));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should capture full element when larger than viewport")]
public async Task ShouldCaptureFullElementWhenLargerThanViewport()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>");
var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-larger-than-viewport.png", screenshot));
await TestUtils.VerifyViewportAsync(Page, 500, 500);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should scroll element into view")]
public async Task ShouldScrollElementIntoView()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 1500px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class=""above""></div>
<div class=""to-screenshot""></div>");
var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-scrolled-into-view.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should scroll 15000px into view")]
public async Task ShouldScroll15000pxIntoView()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 15000px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class=""above""></div>
<div class=""to-screenshot""></div>");
var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-scrolled-into-view.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with a rotated element")]
public async Task ShouldWorkWithARotatedElement()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync(@"
<div style='position: absolute;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
background: green;
transform: rotateZ(200deg); '> </div>
");
var elementHandle = await Page.QuerySelectorAsync("div");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-rotate.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should fail to screenshot a detached element")]
public async Task ShouldFailToScreenshotADetachedElement()
{
await Page.SetContentAsync("<h1>remove this</h1>");
var elementHandle = await Page.QuerySelectorAsync("h1");
await Page.EvaluateAsync("element => element.remove()", elementHandle);
var exception = await PlaywrightAssert.ThrowsAsync<PlaywrightException>(() => elementHandle.ScreenshotAsync());
StringAssert.Contains("Element is not attached to the DOM", exception.Message);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should timeout waiting for visible")]
public async Task ShouldTimeoutWaitingForVisible()
{
await Page.SetContentAsync(@"<div style='width: 50px; height: 0'></div>");
var elementHandle = await Page.QuerySelectorAsync("div");
var exception = await PlaywrightAssert.ThrowsAsync<TimeoutException>(() => elementHandle.ScreenshotAsync(new() { Timeout = 3000 }));
StringAssert.Contains("Timeout 3000ms exceeded", exception.Message);
StringAssert.Contains("element is not visible", exception.Message);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should wait for visible")]
public async Task ShouldWaitForVisible()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
await Page.EvaluateAsync("() => window.scrollBy(50, 100)");
var elementHandle = await Page.QuerySelectorAsync(".box:nth-of-type(3)");
await elementHandle.EvaluateAsync("e => e.style.visibility = 'hidden'");
var task = elementHandle.ScreenshotAsync();
for (int i = 0; i < 10; i++)
{
await Page.EvaluateAsync("() => new Promise(f => requestAnimationFrame(f))");
}
Assert.False(task.IsCompleted);
await elementHandle.EvaluateAsync("e => e.style.visibility = 'visible'");
byte[] screenshot = await task;
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-bounding-box.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work for an element with fractional dimensions")]
public async Task ShouldWorkForAnElementWithFractionalDimensions()
{
await Page.SetContentAsync("<div style=\"width:48.51px;height:19.8px;border:1px solid black;\"></div>");
var elementHandle = await Page.QuerySelectorAsync("div");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-fractional.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with a mobile viewport")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWorkWithAMobileViewport()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
IsMobile = true,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid.html");
await page.EvaluateAsync("() => window.scrollBy(50, 100)");
var elementHandle = await page.QuerySelectorAsync(".box:nth-of-type(3)");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-mobile.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with device scale factor")]
[Skip(SkipAttribute.Targets.Firefox)]
public async Task ShouldWorkWithDeviceScaleFactor()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new()
{
Width = 320,
Height = 480,
},
DeviceScaleFactor = 2,
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid.html");
await page.EvaluateAsync("() => window.scrollBy(50, 100)");
var elementHandle = await page.QuerySelectorAsync(".box:nth-of-type(3)");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-mobile-dsf.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should work for an element with an offset")]
public async Task ShouldWorkForAnElementWithAnOffset()
{
await Page.SetContentAsync("<div style=\"position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;\"></div>");
var elementHandle = await Page.QuerySelectorAsync("div");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-fractional-offset.png", screenshot));
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should take screenshots when default viewport is null")]
public async Task ShouldTakeScreenshotsWhenDefaultViewportIsNull()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = ViewportSize.NoViewport
});
var page = await context.NewPageAsync();
await page.SetContentAsync("<div style='height: 10000px; background: red'></div>");
var windowSize = await page.EvaluateAsync<ViewportSize>("() => ({ width: window.innerWidth * window.devicePixelRatio, height: window.innerHeight * window.devicePixelRatio })");
var sizeBefore = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
byte[] screenshot = await page.ScreenshotAsync();
Assert.NotNull(screenshot);
var decoded = Image.Load(screenshot);
Assert.AreEqual(windowSize.Width, decoded.Width);
Assert.AreEqual(windowSize.Height, decoded.Height);
var sizeAfter = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
Assert.AreEqual(sizeBefore.Width, sizeAfter.Width);
Assert.AreEqual(sizeBefore.Height, sizeAfter.Height);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should take fullPage screenshots when default viewport is null")]
public async Task ShouldTakeFullPageScreenshotsWhenDefaultViewportIsNull()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = ViewportSize.NoViewport
});
var page = await context.NewPageAsync();
await page.GotoAsync(Server.Prefix + "/grid.html");
var sizeBefore = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
byte[] screenshot = await page.ScreenshotAsync(new() { FullPage = true });
Assert.NotNull(screenshot);
var sizeAfter = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
Assert.AreEqual(sizeBefore.Width, sizeAfter.Width);
Assert.AreEqual(sizeBefore.Height, sizeAfter.Height);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should restore default viewport after fullPage screenshot")]
public async Task ShouldRestoreDefaultViewportAfterFullPageScreenshot()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = new() { Width = 456, Height = 789 },
});
var page = await context.NewPageAsync();
await TestUtils.VerifyViewportAsync(page, 456, 789);
byte[] screenshot = await page.ScreenshotAsync(new() { FullPage = true });
Assert.NotNull(screenshot);
await TestUtils.VerifyViewportAsync(page, 456, 789);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should take element screenshot when default viewport is null and restore back")]
public async Task ShouldTakeElementScreenshotWhenDefaultViewportIsNullAndRestoreBack()
{
await using var context = await Browser.NewContextAsync(new()
{
ViewportSize = ViewportSize.NoViewport,
});
var page = await context.NewPageAsync();
await page.SetContentAsync(@"
<div style=""height: 14px"">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>
<div class=""to-screenshot""></div>");
var sizeBefore = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
var elementHandle = await page.QuerySelectorAsync("div.to-screenshot");
byte[] screenshot = await elementHandle.ScreenshotAsync();
Assert.NotNull(screenshot);
var sizeAfter = await page.EvaluateAsync<ViewportSize>("() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })");
Assert.AreEqual(sizeBefore.Width, sizeAfter.Width);
Assert.AreEqual(sizeBefore.Height, sizeAfter.Height);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "should take screenshot of disabled button")]
public async Task ShouldTakeScreenshotOfDisabledButton()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.SetContentAsync("<button disabled>Click me</button>");
var button = await Page.QuerySelectorAsync("button");
byte[] screenshot = await button.ScreenshotAsync();
Assert.NotNull(screenshot);
}
[PlaywrightTest("elementhandle-screenshot.spec.ts", "path option should create subdirectories")]
public async Task PathOptionShouldCreateSubdirectories()
{
await Page.SetViewportSizeAsync(500, 500);
await Page.GotoAsync(Server.Prefix + "/grid.html");
await Page.EvaluateAsync("() => window.scrollBy(50, 100)");
var elementHandle = await Page.QuerySelectorAsync(".box:nth-of-type(3)");
using var tmpDir = new TempDirectory();
string outputPath = Path.Combine(tmpDir.Path, "these", "are", "directories", "screenshot.png");
await elementHandle.ScreenshotAsync(new() { Path = outputPath });
Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-bounding-box.png", outputPath));
}
}
}
/*
* MIT License
*
* Copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using NUnit.Framework;
namespace Microsoft.Playwright.Tests
{
public class RequestFulfillTests : PageTestEx
{
[PlaywrightTest("page-request-fulfill.spec.ts", "should work")]
public async Task ShouldWork()
{
await Page.RouteAsync("**/*", (route) =>
{
route.FulfillAsync(new()
{
Status = (int)HttpStatusCode.Created,
Headers = new Dictionary<string, string>
{
["foo"] = "bar"
},
ContentType = "text/html",
Body = "Yo, page!",
});
});
var response = await Page.GotoAsync(Server.EmptyPage);
Assert.AreEqual((int)HttpStatusCode.Created, response.Status);
#pragma warning disable 0612
Assert.AreEqual("bar", response.Headers["foo"]);
#pragma warning restore 0612
Assert.AreEqual("Yo, page!", await Page.EvaluateAsync<string>("() => document.body.textContent"));
}
/// <summary>
/// In Playwright this method is called ShouldWorkWithStatusCode422.
/// I found that status 422 is not available in all .NET runtime versions (see https://github.com/dotnet/core/blob/4c4642d548074b3fbfd425541a968aadd75fea99/release-notes/2.1/Preview/api-diff/preview2/2.1-preview2_System.Net.md)
/// As the goal here is testing HTTP codes that are not in Chromium (see https://cs.chromium.org/chromium/src/net/http/http_status_code_list.h?sq=package:chromium) we will use code 426: Upgrade Required
/// </summary>
[PlaywrightTest("page-request-fulfill.spec.ts", "should work with status code 422")]
public async Task ShouldWorkWithStatusCode422()
{
await Page.RouteAsync("**/*", (route) =>
{
route.FulfillAsync(new() { Status = (int)HttpStatusCode.UpgradeRequired, Body = "Yo, page!" });
});
var response = await Page.GotoAsync(Server.EmptyPage);
Assert.AreEqual((int)HttpStatusCode.UpgradeRequired, response.Status);
Assert.AreEqual("Upgrade Required", response.StatusText);
Assert.AreEqual("Yo, page!", await Page.EvaluateAsync<string>("() => document.body.textContent"));
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should allow mocking binary responses")]
[Ignore("We need screenshots for this")]
public async Task ShouldAllowMockingBinaryResponses()
{
await Page.RouteAsync("**/*", (route) =>
{
byte[] imageBuffer = File.ReadAllBytes(TestUtils.GetAsset("pptr.png"));
route.FulfillAsync(new()
{
ContentType = "image/png",
BodyBytes = imageBuffer,
});
});
await Page.EvaluateAsync(@"PREFIX => {
const img = document.createElement('img');
img.src = PREFIX + '/does-not-exist.png';
document.body.appendChild(img);
return new Promise(fulfill => img.onload = fulfill);
}", Server.Prefix);
var img = await Page.QuerySelectorAsync("img");
Assert.True(ScreenshotHelper.PixelMatch("mock-binary-response.png", await img.ScreenshotAsync()));
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should allow mocking svg with charset")]
[Ignore("We need screenshots for this")]
public void ShouldAllowMockingSvgWithCharset()
{
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should work with file path")]
[Ignore("We need screenshots for this")]
public async Task ShouldWorkWithFilePath()
{
await Page.RouteAsync("**/*", (route) =>
{
route.FulfillAsync(new()
{
ContentType = "shouldBeIgnored",
Path = TestUtils.GetAsset("pptr.png"),
});
});
await Page.EvaluateAsync(@"PREFIX => {
const img = document.createElement('img');
img.src = PREFIX + '/does-not-exist.png';
document.body.appendChild(img);
return new Promise(fulfill => img.onload = fulfill);
}", Server.Prefix);
var img = await Page.QuerySelectorAsync("img");
Assert.True(ScreenshotHelper.PixelMatch("mock-binary-response.png", await img.ScreenshotAsync()));
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should stringify intercepted request response headers")]
public async Task ShouldStringifyInterceptedRequestResponseHeaders()
{
await Page.RouteAsync("**/*", (route) =>
{
route.FulfillAsync(new()
{
Status = (int)HttpStatusCode.OK,
Headers = new Dictionary<string, string>
{
["foo"] = "true"
},
Body = "Yo, page!",
});
});
var response = await Page.GotoAsync(Server.EmptyPage);
Assert.AreEqual((int)HttpStatusCode.OK, response.Status);
#pragma warning disable 0612
Assert.AreEqual("true", response.Headers["foo"]);
#pragma warning restore 0612
Assert.AreEqual("Yo, page!", await Page.EvaluateAsync<string>("() => document.body.textContent"));
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should not modify the headers sent to the server")]
[Ignore("Flacky with the ASP.NET server")]
public async Task ShouldNotModifyTheHeadersSentToTheServer()
{
await Page.GotoAsync(Server.EmptyPage);
var interceptedRequests = new List<Dictionary<string, string>>();
await Page.GotoAsync(Server.Prefix + "/unused");
Server.SetRoute("/something", ctx =>
{
var hh = new Dictionary<string, string>();
foreach (var h in ctx.Request.Headers)
{
hh[h.Key] = h.Value;
}
interceptedRequests.Add(hh);
ctx.Response.Headers["Access-Control-Allow-Origin"] = "*";
return ctx.Response.WriteAsync("done");
});
string text = await Page.EvaluateAsync<string>(@"async url => {
const data = await fetch(url);
return data.text();
}", Server.CrossProcessPrefix + "/something");
Assert.AreEqual("done", text);
IRequest playwrightRequest = null;
await Page.RouteAsync(Server.CrossProcessPrefix + "/something", (route) =>
{
playwrightRequest = route.Request;
#pragma warning disable 0612
route.ContinueAsync(new() { Headers = route.Request.Headers.ToDictionary(x => x.Key, x => x.Value) });
#pragma warning restore 0612
});
string textAfterRoute = await Page.EvaluateAsync<string>(@"async url => {
const data = await fetch(url);
return data.text();
}", Server.CrossProcessPrefix + "/something");
Assert.AreEqual("done", textAfterRoute);
Assert.AreEqual(2, interceptedRequests.Count);
Assert.AreEqual(interceptedRequests[1].OrderBy(kv => kv.Key), interceptedRequests[0].OrderBy(kv => kv.Key));
}
[PlaywrightTest("page-request-fulfill.spec.ts", "should include the origin header")]
public async Task ShouldIncludeTheOriginHeader()
{
await Page.GotoAsync(Server.EmptyPage);
IRequest interceptedRequest = null;
await Page.RouteAsync(Server.CrossProcessPrefix + "/something", (route) =>
{
interceptedRequest = route.Request;
route.FulfillAsync(new()
{
Headers = new Dictionary<string, string> { ["Access-Control-Allow-Origin"] = "*" },
ContentType = "text/plain",
Body = "done",
});
});
string text = await Page.EvaluateAsync<string>(@"async url => {
const data = await fetch(url);
return data.text();
}", Server.CrossProcessPrefix + "/something");
Assert.AreEqual("done", text);
#pragma warning disable 0612
Assert.AreEqual(Server.Prefix, interceptedRequest.Headers["origin"]);
#pragma warning restore 0612
}
}
}
Accelerate Your Automation Test Cycles With LambdaTest
Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.