Table of Contents

Assertions

Expect makes an assertion that is checked again until it holds, or until its time runs out. Use it for anything the page may still be changing: after a click, the result may take a moment to appear, and an assertion that reads the page once would fail, or pass, by chance.

Expect is a static method of Assertions; bring it in with using static Dramaturge.Assertions;.

Elements

await Expect(page.GetByRole("alert")).ToBeVisibleAsync();
await Expect(page.GetByRole("button", "Submit")).ToBeEnabledAsync();
await Expect(page.GetByLabel("Email")).ToHaveValueAsync("ada@example.com");
await Expect(page.GetByRole("status")).ToHaveTextAsync("Saved");
await Expect(page.GetByRole("listitem")).ToHaveCountAsync(3);
await Expect(page.GetByTestId("total")).ToHaveTextAsync(new Regex(@"^\$\d+\.\d{2}$"));
Matcher Holds when the element
ToBeVisibleAsync, ToBeHiddenAsync Is visible, or hidden; no element at all is hidden
ToBeAttachedAsync Exists in the document
ToBeEnabledAsync, ToBeDisabledAsync Is enabled, or disabled
ToBeEditableAsync Can be edited: neither disabled nor read-only
ToBeCheckedAsync Is a checked checkbox or radio button; a mixed checkbox is not checked
ToBeEmptyAsync Is an input or text area without a value, or an element with no child elements and only white space
ToBeFocusedAsync Has focus in its document or shadow root
ToBeInViewportAsync Is at least partly within its frame's viewport
ToHaveTextAsync, ToContainTextAsync Has, or contains, a text; see Text
ToHaveValueAsync Is an input, text area, or select with a value
ToHaveAttributeAsync Has an attribute, optionally with a value
ToHaveIdAsync, ToHaveClassAsync Has an id, or a whole class attribute
ToHaveCssAsync Has a computed CSS property value
ToHaveCountAsync Is one of exactly the given number of elements the locator finds
ToMatchAriaSnapshotAsync Has an accessibility snapshot that matches a template; see Accessibility Snapshots

Each matcher that compares a value takes a string, for an exact match, or a Regex, which may match anywhere in the value. A matcher about one element throws AmbiguousElementException at once if the locator finds several; ToHaveCountAsync and the list forms of the text matchers work with every match. A matcher that cannot apply to the element, such as ToHaveValueAsync on a paragraph, throws InvalidOperationException at once.

Negation

Not waits until the condition stops holding:

// Waits until the spinner is gone, and fails if it is still showing after the timeout.
await Expect(page.GetByRole("progressbar")).Not.ToBeVisibleAsync();
await Expect(page.GetByRole("status")).Not.ToHaveTextAsync("Saving");

No element at all satisfies the negation of a matcher about an element's state or text, so Not.ToBeVisibleAsync() passes once the element is gone.

Text

// White space is collapsed and trimmed, on the page and in the expected text.
await Expect(page.GetByRole("heading")).ToHaveTextAsync("Order  summary");
await Expect(page.GetByRole("heading")).ToContainTextAsync("summary", ignoreCase: true);

// The rendered text, without text hidden by CSS.
await Expect(page.GetByTestId("price")).ToHaveTextAsync("$10.00", useInnerText: true);

// Every matching element, in order.
await Expect(page.GetByRole("listitem")).ToHaveTextAsync(["Apples", "Bread", "Cheese"]);
await Expect(page.GetByRole("listitem")).ToContainTextAsync(["Apples", "Cheese"]);
  • White space is collapsed and trimmed, in the element's text and in an expected string, so line breaks and indentation in the HTML do not matter. A Regex is matched against the collapsed text, unchanged.
  • Case is compared exactly unless ignoreCase is set. For a Regex, use RegexOptions.IgnoreCase.
  • Hidden text is included: the matchers read textContent. useInnerText: true reads the rendered text instead, without what CSS hides.
  • A list of texts checks every matching element, in order. ToHaveTextAsync requires exactly those texts; ToContainTextAsync requires each text in a later element than the one before, with other elements allowed between.

ToHaveValueAsync, ToHaveAttributeAsync, and the other value matchers compare the value as it is, because a value's white space can matter.

Pages

await Expect(page).ToHaveUrlAsync(new Regex("/orders/\\d+$"));
await Expect(page).ToHaveTitleAsync("Order confirmed");

ToHaveUrlAsync follows the URL the browser reports for each navigation, fragment change, and history update, without sending a command. ToHaveTitleAsync reads the title, with white space collapsed, again after each navigation.

Timeouts and Failures

An expectation retries for ExpectTimeout, 5 seconds unless configured, or the timeout given to the matcher. When it runs out, it throws ExpectationFailedException, which works with any test framework:

try
{
    await Expect(page.GetByRole("status")).ToHaveTextAsync("Saved", timeout: TimeSpan.FromSeconds(2));
}
catch (ExpectationFailedException ex)
{
    // Expected getByRole "status" to have text "Saved"; received "Saving…" after 2 seconds.
    Console.WriteLine(ex.Message);
    Console.WriteLine($"{ex.Expected} / {ex.Actual} / {ex.Timeout}");
}

Expected says what was required, Actual what the last check saw (or null if no element ever matched), and Timeout how long it retried.