From 9ef91653663fb4b50bd6c7b94e6b6c243266770a Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 1 May 2024 20:06:11 +0000 Subject: [PATCH] test: allow updating test snapshots via env (#2927) --- .gitignore | 1 - .../BaseTests/PlaywrightAssert.cs | 53 +++++++++++++ .../ElementHandleScreenshotTests.cs | 26 +++---- .../Helpers/ScreenshotHelper.cs | 72 ------------------ .../PageRequestFulfillTests.cs | 6 +- src/Playwright.Tests/PageScreenshotTests.cs | 56 ++++++++------ .../Screenshots/chromium/grid-cell-5-red.png | Bin 0 -> 479 bytes .../chromium/mock-binary-response.png | Bin 6789 -> 6661 bytes .../Screenshots/chromium/transparent.png | Bin 228 -> 874 bytes .../Screenshots/firefox/grid-cell-5-red.png | Bin 0 -> 479 bytes .../Screenshots/webkit/grid-cell-5-red.png | Bin 0 -> 559 bytes .../webkit/mock-binary-response.png | Bin 6789 -> 6176 bytes .../Screenshots/webkit/transparent.png | Bin 228 -> 2178 bytes 13 files changed, 101 insertions(+), 113 deletions(-) delete mode 100644 src/Playwright.Tests/Helpers/ScreenshotHelper.cs create mode 100644 src/Playwright.Tests/Screenshots/chromium/grid-cell-5-red.png create mode 100644 src/Playwright.Tests/Screenshots/firefox/grid-cell-5-red.png create mode 100644 src/Playwright.Tests/Screenshots/webkit/grid-cell-5-red.png diff --git a/.gitignore b/.gitignore index 467f388e31..4f71cd8c03 100644 --- a/.gitignore +++ b/.gitignore @@ -353,7 +353,6 @@ MigrationBackup/ .idea src/Playwright.sln.DotSettings src/Playwright/.drivers -src/Playwright.Tests/Screenshots/**/test.png src/Playwright/Microsoft.Playwright.xml testCert.cer diff --git a/src/Playwright.Tests/BaseTests/PlaywrightAssert.cs b/src/Playwright.Tests/BaseTests/PlaywrightAssert.cs index 02bb4535f8..67b0d25c4a 100644 --- a/src/Playwright.Tests/BaseTests/PlaywrightAssert.cs +++ b/src/Playwright.Tests/BaseTests/PlaywrightAssert.cs @@ -24,6 +24,8 @@ using System.Text.Json; using NUnit.Framework.Constraints; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; namespace Microsoft.Playwright.Tests; @@ -50,4 +52,55 @@ internal static void AreJsonEqual(object expected, object actual) { Assert.AreEqual(JsonSerializer.Serialize(expected), JsonSerializer.Serialize(actual)); } + + internal static void ToMatchSnapshot(string expectedImageName, string actual) + => ToMatchSnapshot(expectedImageName, File.ReadAllBytes(actual)); + + internal static void ToMatchSnapshot(string expectedImageName, byte[] actual) + { + const int pixelThreshold = 10; + const decimal totalTolerance = 0.05m; + + var goldenPathDir = Path.Combine(TestUtils.FindParentDirectory("Screenshots"), TestConstants.BrowserName); + var expectedImagePath = Path.Combine(goldenPathDir, expectedImageName); + if (Environment.GetEnvironmentVariable("UPDATE_SNAPSHOTS") == "1") + { + File.WriteAllBytes(expectedImagePath, actual); + } + + var expectedImage = Image.Load(expectedImagePath); + var actualImage = Image.Load(actual); + + if (expectedImage.Width != actualImage.Width || expectedImage.Height != actualImage.Height) + { + Assert.Fail("Expected image dimensions do not match actual image dimensions.\n" + + $"Expected: {expectedImage.Width}x{expectedImage.Height}\n" + + $"Actual: {actualImage.Width}x{actualImage.Height}"); + return; + } + + int invalidPixelsCount = 0; + for (int y = 0; y < expectedImage.Height; y++) + { + for (int x = 0; x < expectedImage.Width; x++) + { + var pixelA = expectedImage[x, y]; + var pixelB = actualImage[x, y]; + + + if (Math.Abs(pixelA.R - pixelB.R) > pixelThreshold || + Math.Abs(pixelA.G - pixelB.G) > pixelThreshold || + Math.Abs(pixelA.B - pixelB.B) > pixelThreshold) + { + invalidPixelsCount++; + } + } + } + // cast invalidPixelsCount to decimal to avoid integer division + if (((decimal)invalidPixelsCount / (expectedImage.Height * expectedImage.Width)) > totalTolerance) + { + Assert.Fail($"Expected image to match snapshot but it did not. {invalidPixelsCount} pixels do not match.\n" + + $"Set the environment variable 'UPDATE_SNAPSHOTS' to '1' to update the snapshot."); + } + } } diff --git a/src/Playwright.Tests/ElementHandleScreenshotTests.cs b/src/Playwright.Tests/ElementHandleScreenshotTests.cs index a8c2272a3c..390ffd409a 100644 --- a/src/Playwright.Tests/ElementHandleScreenshotTests.cs +++ b/src/Playwright.Tests/ElementHandleScreenshotTests.cs @@ -37,7 +37,7 @@ public async Task ShouldWork() 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-bounding-box.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should take into account padding and border")] @@ -56,7 +56,7 @@ await Page.SetContentAsync(@"
"); var elementHandle = await Page.QuerySelectorAsync("div#d"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-padding-border.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-padding-border.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should capture full element when larger than viewport in parallel")] @@ -84,7 +84,7 @@ await Page.SetContentAsync(@" 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-larger-than-viewport.png", screenshotTasks.ElementAt(2).Result); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should capture full element when larger than viewport")] @@ -110,7 +110,7 @@ await Page.SetContentAsync(@" var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-larger-than-viewport.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-larger-than-viewport.png", screenshot); await TestUtils.VerifyViewportAsync(Page, 500, 500); } @@ -136,7 +136,7 @@ await Page.SetContentAsync(@"
"); var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-scrolled-into-view.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-scrolled-into-view.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should scroll 15000px into view")] @@ -161,7 +161,7 @@ await Page.SetContentAsync(@"
"); var elementHandle = await Page.QuerySelectorAsync("div.to-screenshot"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-scrolled-into-view.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-scrolled-into-view.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with a rotated element")] @@ -179,7 +179,7 @@ await Page.SetContentAsync(@" "); var elementHandle = await Page.QuerySelectorAsync("div"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-rotate.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-rotate.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should fail to screenshot a detached element")] @@ -221,7 +221,7 @@ public async Task ShouldWaitForVisible() await elementHandle.EvaluateAsync("e => e.style.visibility = 'visible'"); byte[] screenshot = await task; - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-bounding-box.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-bounding-box.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should work for an element with fractional dimensions")] @@ -230,7 +230,7 @@ public async Task ShouldWorkForAnElementWithFractionalDimensions() await Page.SetContentAsync("
"); var elementHandle = await Page.QuerySelectorAsync("div"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-fractional.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-fractional.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with a mobile viewport")] @@ -252,7 +252,7 @@ public async Task ShouldWorkWithAMobileViewport() var elementHandle = await page.QuerySelectorAsync(".box:nth-of-type(3)"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-mobile.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-mobile.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should work with device scale factor")] @@ -274,7 +274,7 @@ public async Task ShouldWorkWithDeviceScaleFactor() 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-mobile-dsf.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should work for an element with an offset")] @@ -283,7 +283,7 @@ public async Task ShouldWorkForAnElementWithAnOffset() await Page.SetContentAsync("
"); var elementHandle = await Page.QuerySelectorAsync("div"); byte[] screenshot = await elementHandle.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-element-fractional-offset.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-fractional-offset.png", screenshot); } [PlaywrightTest("elementhandle-screenshot.spec.ts", "should take screenshots when default viewport is null")] @@ -399,6 +399,6 @@ public async Task PathOptionShouldCreateSubdirectories() 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-element-bounding-box.png", outputPath); } } diff --git a/src/Playwright.Tests/Helpers/ScreenshotHelper.cs b/src/Playwright.Tests/Helpers/ScreenshotHelper.cs deleted file mode 100644 index 8edf264bb9..0000000000 --- a/src/Playwright.Tests/Helpers/ScreenshotHelper.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace Microsoft.Playwright.Tests; - -internal class ScreenshotHelper -{ - internal static bool PixelMatch(string screenShotFile, string fileName) - => PixelMatch(screenShotFile, File.ReadAllBytes(fileName)); - - internal static bool PixelMatch(string screenShotFile, byte[] screenshot) - { - const int pixelThreshold = 10; - const decimal totalTolerance = 0.05m; - - var baseImage = Image.Load(Path.Combine(TestUtils.FindParentDirectory("Screenshots"), TestConstants.BrowserName, screenShotFile)); - var compareImage = Image.Load(screenshot); - - //Just for debugging purpose - compareImage.Save(Path.Combine(TestUtils.FindParentDirectory("Screenshots"), TestConstants.BrowserName, "test.png")); - - if (baseImage.Width != compareImage.Width || baseImage.Height != compareImage.Height) - { - return false; - } - - int invalidPixelsCount = 0; - - for (int y = 0; y < baseImage.Height; y++) - { - for (int x = 0; x < baseImage.Width; x++) - { - var pixelA = baseImage[x, y]; - var pixelB = compareImage[x, y]; - - - if (Math.Abs(pixelA.R - pixelB.R) > pixelThreshold || - Math.Abs(pixelA.G - pixelB.G) > pixelThreshold || - Math.Abs(pixelA.B - pixelB.B) > pixelThreshold) - { - invalidPixelsCount++; - } - } - } - - return (invalidPixelsCount / (baseImage.Height * baseImage.Width)) < totalTolerance; - } -} diff --git a/src/Playwright.Tests/PageRequestFulfillTests.cs b/src/Playwright.Tests/PageRequestFulfillTests.cs index 14cef84d8a..0499556cc4 100644 --- a/src/Playwright.Tests/PageRequestFulfillTests.cs +++ b/src/Playwright.Tests/PageRequestFulfillTests.cs @@ -27,7 +27,7 @@ namespace Microsoft.Playwright.Tests; -public class RequestFulfillTests : PageTestEx +public class PageRequestFulfillTests : PageTestEx { [PlaywrightTest("page-request-fulfill.spec.ts", "should work")] public async Task ShouldWork() @@ -88,7 +88,7 @@ await Page.EvaluateAsync(@"PREFIX => { 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())); + PlaywrightAssert.ToMatchSnapshot("mock-binary-response.png", await img.ScreenshotAsync()); } [PlaywrightTest("page-request-fulfill.spec.ts", "should allow mocking svg with charset")] @@ -116,7 +116,7 @@ await Page.EvaluateAsync(@"PREFIX => { 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())); + PlaywrightAssert.ToMatchSnapshot("mock-binary-response.png", await img.ScreenshotAsync()); } [PlaywrightTest("page-request-fulfill.spec.ts", "should stringify intercepted request response headers")] diff --git a/src/Playwright.Tests/PageScreenshotTests.cs b/src/Playwright.Tests/PageScreenshotTests.cs index 039554aaa4..b43fb3a3c7 100644 --- a/src/Playwright.Tests/PageScreenshotTests.cs +++ b/src/Playwright.Tests/PageScreenshotTests.cs @@ -35,7 +35,7 @@ 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-sanity.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should clip rect")] @@ -54,7 +54,7 @@ public async Task ShouldClipRect() } } ); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-rect.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-clip-rect.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should clip rect with fullPage")] @@ -74,7 +74,7 @@ public async Task ShouldClipRectWithFullPage() Height = 100, } }); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-rect.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-clip-rect.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should clip elements to the viewport")] @@ -92,7 +92,7 @@ public async Task ShouldClipElementsToTheViewport() Height = 100, } }); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-offscreen-clip.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-offscreen-clip.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should throw on clip outside the viewport")] @@ -136,7 +136,7 @@ public async Task ShouldRunInParallel() } await TaskUtils.WhenAll(tasks); - Assert.True(ScreenshotHelper.PixelMatch("grid-cell-1.png", tasks[0].Result)); + PlaywrightAssert.ToMatchSnapshot("grid-cell-1.png", tasks[0].Result); } [PlaywrightTest("page-screenshot.spec.ts", "should take fullPage screenshots")] @@ -145,7 +145,7 @@ 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-grid-fullpage.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should restore viewport after fullPage screenshot")] @@ -197,7 +197,7 @@ async Task Func() for (int i = 0; i < n; i++) { - Assert.True(ScreenshotHelper.PixelMatch($"grid-cell-{i % 2}.png", screenshotTasks[i].Result)); + PlaywrightAssert.ToMatchSnapshot($"grid-cell-{i % 2}.png", screenshotTasks[i].Result); } var closeTasks = new List(); @@ -213,11 +213,19 @@ async Task Func() [Skip(SkipAttribute.Targets.Firefox)] public async Task ShouldAllowTransparency() { - await Page.SetViewportSizeAsync(50, 150); - await Page.GotoAsync(Server.EmptyPage); + await Page.SetViewportSizeAsync(300, 300); + await Page.SetContentAsync(@" + +
+
+
+ "); byte[] screenshot = await Page.ScreenshotAsync(new() { OmitBackground = true }); - Assert.True(ScreenshotHelper.PixelMatch("transparent.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("transparent.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should render white background on jpeg file")] @@ -230,7 +238,7 @@ public async Task ShouldRenderWhiteBackgroundOnJpegFile() OmitBackground = true, Type = ScreenshotType.Jpeg, }); - Assert.True(ScreenshotHelper.PixelMatch("white.jpg", screenshot)); + PlaywrightAssert.ToMatchSnapshot("white.jpg", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work with odd clip size on Retina displays")] @@ -247,7 +255,7 @@ public async Task ShouldWorkWithOddClipSizeOnRetinaDisplays() } }); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-clip-odd-size.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-clip-odd-size.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport")] @@ -267,7 +275,7 @@ public async Task ShouldWorkWithAMobileViewport() await page.GotoAsync(Server.Prefix + "/overflow.html"); byte[] screenshot = await page.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-mobile.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-mobile.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport and clip")] @@ -296,7 +304,7 @@ public async Task ShouldWorkWithAMobileViewportAndClip() } }); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-mobile-clip.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-mobile-clip.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work with a mobile viewport and fullPage")] @@ -316,7 +324,7 @@ public async Task ShouldWorkWithAMobileViewportAndFullPage() 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-mobile-fullpage.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work for canvas")] @@ -326,7 +334,7 @@ public async Task ShouldWorkForCanvas() await Page.GotoAsync(Server.Prefix + "/screenshots/canvas.html"); byte[] screenshot = await Page.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-canvas.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-canvas.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work for translateZ")] @@ -336,7 +344,7 @@ public async Task ShouldWorkForTranslateZ() await Page.GotoAsync(Server.Prefix + "/screenshots/translateZ.html"); byte[] screenshot = await Page.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-translateZ.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-translateZ.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work while navigating")] @@ -373,7 +381,7 @@ public async Task ShouldWorkWithDeviceScaleFactor() await page.GotoAsync(Server.Prefix + "/grid.html"); byte[] screenshot = await page.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-device-scale-factor.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-device-scale-factor.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "should work with iframe in shadow")] @@ -391,7 +399,7 @@ public async Task ShouldWorkWithiFrameInShadow() await page.GotoAsync(Server.Prefix + "/grid-iframe-in-shadow.html"); byte[] screenshot = await page.ScreenshotAsync(); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-iframe.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("screenshot-iframe.png", screenshot); } [PlaywrightTest("page-screenshot.spec.ts", "path option should work")] @@ -403,7 +411,7 @@ public async Task PathOptionShouldWork() string outputPath = Path.Combine(tmpDir.Path, "screenshot.png"); await Page.ScreenshotAsync(new() { Path = outputPath }); - Assert.True(ScreenshotHelper.PixelMatch("screenshot-sanity.png", outputPath)); + PlaywrightAssert.ToMatchSnapshot("screenshot-sanity.png", outputPath); } [PlaywrightTest("page-screenshot.spec.ts", "path option should create subdirectories")] @@ -415,7 +423,7 @@ public async Task PathOptionShouldCreateSubdirectories() 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)); + PlaywrightAssert.ToMatchSnapshot("screenshot-sanity.png", outputPath); } [PlaywrightTest("page-screenshot.spec.ts", "path option should detect joeg")] @@ -427,7 +435,7 @@ public async Task PathOptionShouldDetectJpeg() string outputPath = Path.Combine(tmpDir.Path, "screenshot.jpg"); await Page.ScreenshotAsync(new() { Path = outputPath, OmitBackground = true }); - Assert.True(ScreenshotHelper.PixelMatch("white.jpg", outputPath)); + PlaywrightAssert.ToMatchSnapshot("white.jpg", outputPath); } [PlaywrightTest("page-screenshot.spec.ts", "path option should throw for unsupported mime type")] @@ -446,7 +454,7 @@ public async Task ShouldHideElementsBasedOnAttr() var screenshot = await Page.ScreenshotAsync(new() { Style = @"[data-test-screenshot=""hide""] { visibility: hidden; }" }); - Assert.True(ScreenshotHelper.PixelMatch("hide-should-work.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("hide-should-work.png", screenshot); var visibility = await Page.Locator("div").Nth(5).EvaluateAsync("element => element.style.visibility"); Assert.AreEqual("", visibility); } @@ -460,7 +468,7 @@ public async Task ShouldRemoveElementsBasedOnAttr() var screenshot = await Page.ScreenshotAsync(new() { Style = @"[data-test-screenshot=""remove""] { visibility: none; }" }); - Assert.True(ScreenshotHelper.PixelMatch("remove-should-work.png", screenshot)); + PlaywrightAssert.ToMatchSnapshot("remove-should-work.png", screenshot); var display = await Page.Locator("div").Nth(5).EvaluateAsync("element => element.style.display"); Assert.AreEqual("", display); } diff --git a/src/Playwright.Tests/Screenshots/chromium/grid-cell-5-red.png b/src/Playwright.Tests/Screenshots/chromium/grid-cell-5-red.png new file mode 100644 index 0000000000000000000000000000000000000000..5793adf6d1af6966c441262ae3c14062820aed3a GIT binary patch literal 479 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 S5dr?a1g9*oAy*+|8S+0LA10Fk diff --git a/src/Playwright.Tests/Screenshots/chromium/transparent.png b/src/Playwright.Tests/Screenshots/chromium/transparent.png index 6c1fe85b52f24d502ca9f191fc831470f1deff3d..b9aa8f7a7260c5f25fcb19c8ede467c5abe7e064 100644 GIT binary patch literal 874 zcmeAS@N?(olHy`uVBq!ia0y~yVAKI&4mO}jWo=(6kYX$ja(7}_cTVOd0|T>)r;B4q z#hka-HgXr_|?=~}oAE(#^#R!MC1V+gw))bCW z4ryUu|zNHG=%xjQkeJ16rJ$eHKq;uumf z=k2Y7oDBgytOqr|-T$I7x(W(d^&OY796S2r!)cI!r>mdKI;Vst0P{snM*si- diff --git a/src/Playwright.Tests/Screenshots/firefox/grid-cell-5-red.png b/src/Playwright.Tests/Screenshots/firefox/grid-cell-5-red.png new file mode 100644 index 0000000000000000000000000000000000000000..5793adf6d1af6966c441262ae3c14062820aed3a GIT binary patch literal 479 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?4jBOuH;Rhv(mfq`+N zr;B4q#hkadHf9|P5NZGT|ACgD)y?f0F|TGhg^GwQy8DSik57_C{dLH3tsR!vmRLnN zn(wyAEEK3=FDxv|d%w4}DRNiEslapQ8ZqsM`|mvIlK5ErpF2FjWxvH!BfXsmzSk>U zpWv`VRUnVEC7S8sG&u9;>g@^#3M^c7#D2%#ep^;C-^b(GM8{>9HJMsDzweBCcx}@; zyMCTcaqAye%FVm|_A)<*fR)jSw9SsIuQDAHSmdFw`l*n|(h29EPxMgPaR2?))(scl zmL;w%?eb8#{r1`(_r-x%UYE-7v75~HP4#YkwOw7v!$!`1@x=p^??r8$6}dZJXM*4I zj}a>$lp0w4_7UEa*%Hn4a2lNXbM?xU#am+mF<}{V-e@qk3ULW z&6*07JyIH5EOAd|@{8}kpS`l^OV0Z@-LD;_OL6kS3==2MT;1E-&d#d)qjz)83_Xr6rZ_?haXCX_j_DL0U?1326}NPL*6(V(AV+x{(fPlw3hNq~YUt z^S}Bo=FFTkGZ*JP&v|FwiPFJYI0qgHto8$6uHbt2%djCO90?$v^rSH@B?t)95>aluW?0g8V{1lsPukJDingFu8FPG8Ts)rfFze@L-=%r z^zMrw`jZvab9E9dB1U0qz4z(mWOhaOtHs5OIXO#m8y3H<-Ns;Y-max)4S%x?I1G&j zlk^l9NFYOA#%@-4pB{)nf9I+O%-*$y$@%@WoIog{%$g2zeeMMO#93G{W2pGLZw@;W z*tIi$_mb;^hmzS&?*pQAUF%&}q%S%#pEOhP!J73rS>53cPIF#!7C~({_cw=dzdM1} z+?8c7f}v%} zJz=2?mDC4%9i^DSQlql;)`OP6%O_huQYbwkhYXT#odN$A>|JM2(1UsF1W!&1uRrsH zVXm@sYiF-PTbfcK(`6l&%?K zxW-==%lQ)fpa;7%vo7RtF5fPWmRjCla!mR!uc-69VEdr*dmX`Q%PtJ999bHJVx2yL zRxgZ-T_z8hH$qbc;o)u>Bz@EaYc9zJKIcD%b0K zb>sc1Tn530yKe#+MYnAb!d-)dSi)E2g(TEZ#&C1VvvjYH#Za#+T0)S*v|^4n3D5Qv zzLxiV_c=Yf+(_8S_FnC3TxrEqT~X-dR1p+jm}~DasO?Wlqx;RSjlG%^^P;4r`Fgj^ zeOg;$3-ubL*XX?P@nEHOW6Aahb67eL_L*bc1Q!QD+Yz7!l^~?omFi?dSKF5wRfNCk z38(4Q_reXu>R<1RIo?dsUElg_(f>1BCOG}2r3agRZ)jcu0ZB-mBe~+3*SnfiW7kbF zOUb_%-Gl;1NxT!a+%$L^<2iE2qpSM}bByVBL1@jBe-2?2C9guZ8-lyTK6btApw!6{ z342~_YE%KERE_}kNujesi?lQKiKBY%>G5N$I__z8<<2QqKWq>?&kw3L6n!B&#An4+ z&b~PHZT3b3509F>2=yHe!H!EfklcGV)iau4bdDeYJH*+aNleWiKhg{j{OVJaW{LS5 z?k~PrZ1FfPO^Ob9TaC6Zh(YL3mrxBXW;l(9PLf3}9$aTPdV&>?tDMCoC{7UTYsAc; z%n)InFvQQ^4Qt~keQ;;^W(7K0^)K_*7~LQ_n8hi{&ggVmi3%Xk>8ozXJniRd_1do; z4w8lAOq2+{VDSa+?_y>)_LPJi>c6SrknD>g%Sy*19>tVAw08&fmFgF2Pz&4OVS&^d z=-Uds9`D0uFD)B2e7}`W2c!~Atx&fCP+M&++eJm2&8e2-)@_Uc3>oN`|(%Y<{ znr{!^X2c>q_AO!35SqVZPk7mL?v<|$-4@C7mAkcw=dnf>zB_EuzkCtOET6*9H*0{^ zKDb>>=tu@GZX6wn_wk)?jW~elYCWKOjj$#DnEL=jgn_Oc<$o{e@IWnzvGGHoBlcs> zTk>;27as8e#+j3v;IJyU0Nd^mT$Pd9h2%hSjFEbW5U-yjhEs=bGRvy`(6OG^q%uo@ z%a;hN?^oge8{y(LpS~8c%`f3t#kii@%9cNLBzK6<2cO2+~frZpHBt9}B{d^SmKs5G!qC?firO@Wuc2SI{v(E#XJ9-ZD93Xt8C zd{usG)fdj-IRl;V2vT6obRg*~2*S+(ehbdi@%b!moq~(x>#~d>jXm@E#T+^;n$lAp zZXa?#A~J5uap}b-y^T-Nc6)m|zH|ip{=uCdJ|bk=`n9}*R55G%tMWnP3h9`TTVo;| zPiKyI>}@uoq<(m+C;2oGLm7ve3!-HcshxzuX0JD1I8#Z#M)bahq8t~qOah7RnSRSi zyb+4~1V=ldps3aWp6AAYfoz&9f7t_a6EBy){2iKzk=oZ9cv}!zq3HGkeO~ukTt&i@ z|LESo`Ktwa);4L6#%~ICG(_ndhdg-V3Nxhc8hnKvqNF-KB)dT6Ys_|)E+omxyX z<92)qLP@<%H75o~=ZAzwfq%KOq0E{l zh6NDPQ(3bnSC`tWN3{R)Y~FthsTd>u^KxR49A5`r4h@#$#=hAhQ}i06>vwEkx_x zfDe*FkE|+=YPdDY>8>anWDy5f8u2R2kuUl3@S~r<{6Wumv@H7jA^=zzX|Ct{rjfSTa4z#quSxD5aLj#* zFKS%mjgk*0T5Zl%{cnGXg&wpq z@I0vZY-hRz_b0e{`(#brJadlruS5y!0?p2u&0DEV9wwDPJOYWLGY0g^a%Pj$*_sz` zK=kL~?Q<1o5-65ZBbEt#NEch2^HS5h2rwkw(>0YtuRzxjF<58q+_ek5V%PsJJTUj; zPcl-xfNr9WO)niPaPG^?h!JWPRd3|MVh1El$E4R8)kAWP8>@95`xZG$ed^D_a6=hW zGln7)MjpPw(d&CqkvK2fHb5-fkx8kaFeZ<@nz0 zH`UnQh$CpaF6Y#-kSb(jmK(h^!YwwLB(W4%LUB&a@GEXb7J?N#**m7J148<185A8r zr{M(89n#+!fA$f*anpqU9Zu)kSvM~4)QN|hz;_UlV-QYutk5{|j)=w>+Gp!{6h(D3MZ_sJe`NM78im*Q zCTYcG)H=7Yvr4h-iDZ<2C&G;r;jZ7ds6@nVi?iIi@i||$h}ziuP$trG`qVnG(Z0{I z36Eizw$*Z&Pa}r};o53|=ViRk630lEM2gmn^t5J9F*cfO@upir-ma=_nri!Xg^=7v zJe@aDyA*M2a?E47XHR|!1t0;?EB(!^_?K2H*~qoUm{eE^oHW8=L)d4SzM{av=$+@T z+{JRJra+tMiFtB|x)M}FIod#cFqus=*o~W@0TmOQ_iAk(`7Jbwja{^b*|MIQTARgk z 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 S5dr?a1g9*oAy*+|8S+0LA10Fk diff --git a/src/Playwright.Tests/Screenshots/webkit/transparent.png b/src/Playwright.Tests/Screenshots/webkit/transparent.png index 6c1fe85b52f24d502ca9f191fc831470f1deff3d..e8bc1c8f360b41f7c0d5ae1a1a08ccf65d70c5ab 100644 GIT binary patch literal 2178 zcmeAS@N?(olHy`uVBq!ia0y~yVAKI&4mO}jWo=(6kYX$ja(7}_cTVOdkmHgX;hE;^ z%b*2hb1*QrXELyWlmM|55Hm0^FJNR~2GWcmu?0*pSw@`&%m_9};f;i*=L`(&Z#-Qb zLn;{GUc1QI;2_|9F!;^>r=bVs_OL7ekXQ5Gk-XOQ{Cj1Gh6CT2o`2(HRROZp6E@6Y zVq}tJY@WK(D}afGXPUHvgMxCx2BT0;uZD($Ma(Q5DJ(o;%d{Lh1q>qc8W2|F2?Sd5t1w1cNEVBwI^F?VQia66EYx=LsY17q`3 zHV%Ok91?4MLYssX5;ojn7`1dXc&VL`*3De#08F7R4dP*o)D$@dDp+}@S&ydY(VROh r$|zuw$-*I^08HVdj1e9PjsIEmB(zxj?BjV`K=r?;tDnm{r-UW|c&QG8 literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^MnF7`gAGW|*>u|zNHG=%xjQkeJ16rJ$eHKq;uumf z=k2Y7oDBgytOqr|-T$I7x(W(d^&OY796S2r!)cI!r>mdKI;Vst0P{snM*si-