diff --git a/src/Playwright.MSTest/PlaywrightTest.cs b/src/Playwright.MSTest/PlaywrightTest.cs
index e60c5cbd8..b0a7f59d7 100644
--- a/src/Playwright.MSTest/PlaywrightTest.cs
+++ b/src/Playwright.MSTest/PlaywrightTest.cs
@@ -53,4 +53,10 @@ public async Task PlaywrightSetup()
public IPageAssertions Expect(IPage page) => Assertions.Expect(page);
public IAPIResponseAssertions Expect(IAPIResponse response) => Assertions.Expect(response);
+
+ public ILocatorAssertions Expect(ILocator locator, string message) => Assertions.Expect(locator, message);
+
+ public IPageAssertions Expect(IPage page, string message) => Assertions.Expect(page, message);
+
+ public IAPIResponseAssertions Expect(IAPIResponse response, string message) => Assertions.Expect(response, message);
}
diff --git a/src/Playwright.NUnit/PlaywrightTest.cs b/src/Playwright.NUnit/PlaywrightTest.cs
index 94526676f..15cf59a48 100644
--- a/src/Playwright.NUnit/PlaywrightTest.cs
+++ b/src/Playwright.NUnit/PlaywrightTest.cs
@@ -53,4 +53,10 @@ public async Task PlaywrightSetup()
public IPageAssertions Expect(IPage page) => Assertions.Expect(page);
public IAPIResponseAssertions Expect(IAPIResponse response) => Assertions.Expect(response);
+
+ public ILocatorAssertions Expect(ILocator locator, string message) => Assertions.Expect(locator, message);
+
+ public IPageAssertions Expect(IPage page, string message) => Assertions.Expect(page, message);
+
+ public IAPIResponseAssertions Expect(IAPIResponse response, string message) => Assertions.Expect(response, message);
}
diff --git a/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs b/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs
index 97eea0aff..b84b58df6 100644
--- a/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs
+++ b/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs
@@ -73,6 +73,24 @@ public async Task FailWithIndeterminateTrue()
StringAssert.Contains("Expect \"ToBeCheckedAsync\" with timeout 1000ms", exception.Message);
}
+ [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support a custom expect message")]
+ public async Task ShouldSupportCustomExpectMessage()
+ {
+ await Page.SetContentAsync("
");
+ var locator = Page.Locator("input");
+ var exception = await PlaywrightAssert.ThrowsAsync(
+ () => Expect(locator, "should be logged in").ToBeVisibleAsync(new() { Timeout = 1000 }));
+ StringAssert.StartsWith("should be logged in", exception.Message);
+ StringAssert.Contains("Locator expected to be visible", exception.Message);
+
+ // Custom message also propagates through Not.
+ await Page.SetContentAsync("");
+ var present = Page.Locator("input");
+ var notException = await PlaywrightAssert.ThrowsAsync(
+ () => Expect(present, "should not be present").Not.ToBeVisibleAsync(new() { Timeout = 1000 }));
+ StringAssert.StartsWith("should not be present", notException.Message);
+ }
+
[PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should be able to set default timeout")]
public async Task ShouldBeAbleToSetDefaultTimeout()
{
diff --git a/src/Playwright.Xunit.v3/PlaywrightTest.cs b/src/Playwright.Xunit.v3/PlaywrightTest.cs
index 0c99831ca..532f79245 100644
--- a/src/Playwright.Xunit.v3/PlaywrightTest.cs
+++ b/src/Playwright.Xunit.v3/PlaywrightTest.cs
@@ -52,4 +52,10 @@ public override async ValueTask InitializeAsync()
public IPageAssertions Expect(IPage page) => Assertions.Expect(page);
public IAPIResponseAssertions Expect(IAPIResponse response) => Assertions.Expect(response);
+
+ public ILocatorAssertions Expect(ILocator locator, string message) => Assertions.Expect(locator, message);
+
+ public IPageAssertions Expect(IPage page, string message) => Assertions.Expect(page, message);
+
+ public IAPIResponseAssertions Expect(IAPIResponse response, string message) => Assertions.Expect(response, message);
}
diff --git a/src/Playwright.Xunit/PlaywrightTest.cs b/src/Playwright.Xunit/PlaywrightTest.cs
index e4b8ae5b1..08967132f 100644
--- a/src/Playwright.Xunit/PlaywrightTest.cs
+++ b/src/Playwright.Xunit/PlaywrightTest.cs
@@ -52,4 +52,10 @@ public override async Task InitializeAsync()
public IPageAssertions Expect(IPage page) => Assertions.Expect(page);
public IAPIResponseAssertions Expect(IAPIResponse response) => Assertions.Expect(response);
+
+ public ILocatorAssertions Expect(ILocator locator, string message) => Assertions.Expect(locator, message);
+
+ public IPageAssertions Expect(IPage page, string message) => Assertions.Expect(page, message);
+
+ public IAPIResponseAssertions Expect(IAPIResponse response, string message) => Assertions.Expect(response, message);
}
diff --git a/src/Playwright/Assertions.cs b/src/Playwright/Assertions.cs
index 9fa016b3f..12c51c9ad 100644
--- a/src/Playwright/Assertions.cs
+++ b/src/Playwright/Assertions.cs
@@ -44,4 +44,23 @@ public static class Assertions
public static IPageAssertions Expect(IPage page) => new PageAssertions(page, false);
public static IAPIResponseAssertions Expect(IAPIResponse response) => new APIResponseAssertions(response, false);
+
+ ///
+ /// Creates assertions that prefix any failure message with , providing
+ /// extra context in test reports.
+ ///
+ /// The locator to assert against.
+ /// Message to prepend to any assertion failure.
+ /// Assertions for the given locator.
+ public static ILocatorAssertions Expect(ILocator locator, string message) => new LocatorAssertions(locator, false, message);
+
+ ///
+ /// The page to assert against.
+ /// Message to prepend to any assertion failure.
+ public static IPageAssertions Expect(IPage page, string message) => new PageAssertions(page, false, message);
+
+ ///
+ /// The API response to assert against.
+ /// Message to prepend to any assertion failure.
+ public static IAPIResponseAssertions Expect(IAPIResponse response, string message) => new APIResponseAssertions(response, false, message);
}
diff --git a/src/Playwright/Core/APIResponseAssertions.cs b/src/Playwright/Core/APIResponseAssertions.cs
index e12998000..01c324c43 100644
--- a/src/Playwright/Core/APIResponseAssertions.cs
+++ b/src/Playwright/Core/APIResponseAssertions.cs
@@ -33,8 +33,9 @@ internal class APIResponseAssertions : IAPIResponseAssertions
{
private readonly APIResponse _actual;
private readonly bool _isNot;
+ private readonly string? _customMessage;
- public APIResponseAssertions(IAPIResponse response, bool isNot)
+ public APIResponseAssertions(IAPIResponse response, bool isNot, string? customMessage = null)
{
if (response == null)
{
@@ -42,9 +43,10 @@ public APIResponseAssertions(IAPIResponse response, bool isNot)
}
_actual = (APIResponse)response;
_isNot = isNot;
+ _customMessage = customMessage;
}
- public IAPIResponseAssertions Not => new APIResponseAssertions(_actual, !_isNot);
+ public IAPIResponseAssertions Not => new APIResponseAssertions(_actual, !_isNot, _customMessage);
public async Task ToBeOKAsync()
{
@@ -72,7 +74,8 @@ public async Task ToBeOKAsync()
responseText = $"\nResponse text:\n{trimmedText}";
}
}
- throw new PlaywrightException(message + responseText);
+ var prefix = _customMessage != null ? _customMessage + "\n\n" : string.Empty;
+ throw new PlaywrightException(prefix + message + responseText);
}
private static bool IsTextualMimeType(string contentType)
diff --git a/src/Playwright/Core/AssertionsBase.cs b/src/Playwright/Core/AssertionsBase.cs
index bfb1a2b9e..c6527835b 100644
--- a/src/Playwright/Core/AssertionsBase.cs
+++ b/src/Playwright/Core/AssertionsBase.cs
@@ -44,13 +44,16 @@ internal abstract class AssertionsBase
{
private static float _defaultTimeout = 5_000;
- public AssertionsBase(bool isNot)
+ public AssertionsBase(bool isNot, string? customMessage = null)
{
IsNot = isNot;
+ CustomMessage = customMessage;
}
protected bool IsNot { get; }
+ protected string? CustomMessage { get; }
+
protected abstract Task CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title);
protected async Task ExpectImplAsync(string expression, ExpectedTextValue textValue, object expected, string message, string title, FrameExpectOptions options)
@@ -85,11 +88,12 @@ protected async Task ExpectImplAsync(string expression, FrameExpectOptions expec
{
message += "\n" + result.ErrorMessage;
}
+ var prefix = CustomMessage != null ? CustomMessage + "\n\n" : string.Empty;
if (expected == null)
{
- throw new PlaywrightException($"{message} {log}");
+ throw new PlaywrightException($"{prefix}{message} {log}");
}
- throw new PlaywrightException($"{message} '{FormatValue(expected)}'\nBut was: '{FormatValue(actual)}' {log}");
+ throw new PlaywrightException($"{prefix}{message} '{FormatValue(expected)}'\nBut was: '{FormatValue(actual)}' {log}");
}
}
diff --git a/src/Playwright/Core/LocatorAssertions.cs b/src/Playwright/Core/LocatorAssertions.cs
index 9ebc9beae..f1aa62c0c 100644
--- a/src/Playwright/Core/LocatorAssertions.cs
+++ b/src/Playwright/Core/LocatorAssertions.cs
@@ -32,14 +32,14 @@ namespace Microsoft.Playwright.Core;
internal class LocatorAssertions : AssertionsBase, ILocatorAssertions
{
- public LocatorAssertions(ILocator locator, bool isNot) : base(isNot)
+ public LocatorAssertions(ILocator locator, bool isNot, string? customMessage = null) : base(isNot, customMessage)
{
ActualLocator = (Locator)locator;
}
private Locator ActualLocator { get; }
- public ILocatorAssertions Not => new LocatorAssertions(ActualLocator, !IsNot);
+ public ILocatorAssertions Not => new LocatorAssertions(ActualLocator, !IsNot, CustomMessage);
protected override Task CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title)
{
diff --git a/src/Playwright/Core/PageAssertions.cs b/src/Playwright/Core/PageAssertions.cs
index a20c1e30e..8d283fca9 100644
--- a/src/Playwright/Core/PageAssertions.cs
+++ b/src/Playwright/Core/PageAssertions.cs
@@ -33,12 +33,12 @@ internal class PageAssertions : AssertionsBase, IPageAssertions
{
private readonly Page _page;
- public PageAssertions(IPage page, bool isNot) : base(isNot)
+ public PageAssertions(IPage page, bool isNot, string? customMessage = null) : base(isNot, customMessage)
{
_page = (Page)page;
}
- public IPageAssertions Not => new PageAssertions(_page, !IsNot);
+ public IPageAssertions Not => new PageAssertions(_page, !IsNot, CustomMessage);
protected override Task CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title)
{