Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Playwright.MSTest/PlaywrightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
6 changes: 6 additions & 0 deletions src/Playwright.NUnit/PlaywrightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
18 changes: 18 additions & 0 deletions src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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("<div></div>");
var locator = Page.Locator("input");
var exception = await PlaywrightAssert.ThrowsAsync<PlaywrightException>(
() => 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("<input>");
var present = Page.Locator("input");
var notException = await PlaywrightAssert.ThrowsAsync<PlaywrightException>(
() => 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()
{
Expand Down
6 changes: 6 additions & 0 deletions src/Playwright.Xunit.v3/PlaywrightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
6 changes: 6 additions & 0 deletions src/Playwright.Xunit/PlaywrightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
19 changes: 19 additions & 0 deletions src/Playwright/Assertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>
/// Creates assertions that prefix any failure message with <paramref name="message"/>, providing
/// extra context in test reports.
/// </summary>
/// <param name="locator">The locator to assert against.</param>
/// <param name="message">Message to prepend to any assertion failure.</param>
/// <returns>Assertions for the given locator.</returns>
public static ILocatorAssertions Expect(ILocator locator, string message) => new LocatorAssertions(locator, false, message);

/// <inheritdoc cref="Expect(ILocator, string)" />
/// <param name="page">The page to assert against.</param>
/// <param name="message">Message to prepend to any assertion failure.</param>
public static IPageAssertions Expect(IPage page, string message) => new PageAssertions(page, false, message);

/// <inheritdoc cref="Expect(ILocator, string)" />
/// <param name="response">The API response to assert against.</param>
/// <param name="message">Message to prepend to any assertion failure.</param>
public static IAPIResponseAssertions Expect(IAPIResponse response, string message) => new APIResponseAssertions(response, false, message);
}
9 changes: 6 additions & 3 deletions src/Playwright/Core/APIResponseAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,20 @@ 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)
{
throw new ArgumentNullException(nameof(response));
}
_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()
{
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 7 additions & 3 deletions src/Playwright/Core/AssertionsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FrameExpectResult> CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title);

protected async Task ExpectImplAsync(string expression, ExpectedTextValue textValue, object expected, string message, string title, FrameExpectOptions options)
Expand Down Expand Up @@ -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}");
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Playwright/Core/LocatorAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FrameExpectResult> CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Playwright/Core/PageAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FrameExpectResult> CallExpectAsync(string expression, FrameExpectOptions expectOptions, string title)
{
Expand Down
Loading