You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
expect(locator).to_contain_text(...) and to_have_text(...) compare raw inner_text() output with only outer whitespace trimmed, then do a plain substring/equality check. Upstream Playwright normalizes internal whitespace (collapsing runs of whitespace, including newlines, into single spaces) before matching toContainText/toHaveText, so elements that render text across multiple lines still match a single-line expectation. playwright-rust's assertions don't do this, causing spurious failures (and a full retry-timeout wait) on any element whose text content spans multiple lines or has irregular internal spacing.
to_have_text (lines 210-250) does the same thing with equality instead of contains, and additionally only trims the expected string's outer whitespace (expected.trim()), not its internal whitespace. Neither method collapses internal whitespace/newlines in actual or expected.
Code that triggered this (from Folio's crates/folio-cli/tests/browser_preview_test.rs):
let progress = page.locator(".workflow-progress").await?;expect(&progress).to_contain_text("Scan → Group → Name → Review → Copy").await?;
Error/Output:
Expected element '.workflow-progress' to contain text 'Scan → Group → Name → Review → Copy', but had 'Scan\n→\nGroup\n→\nName\n→\nReview\n→\nCopy' after 5s.
The element's text content is semantically identical once whitespace is normalized, but the assertion burns the full 5s retry/timeout window before failing with a confusing error.
Expected Behavior
Whitespace (newlines, tabs, runs of spaces) should be collapsed to single spaces in both the actual DOM text and the expected string before the string-argument comparison in to_have_text and to_contain_text, matching upstream Playwright's documented behavior.
Upstream playwright behavior (JS):
// From https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-contain-text// "This method also performs whitespace normalization."awaitexpect(page.locator('.workflow-progress')).toContainText('Scan → Group → Name → Review → Copy');// Passes even if the DOM text is "Scan\n→\nGroup\n→\nName\n→\nReview\n→\nCopy"
Note: the regex variants (to_have_text_regex, to_contain_text_regex) should not be changed — upstream matches regex patterns against the raw (non-normalized) text, so this normalization should only apply to the plain-string overloads.
Current Workaround: Fetch text_content() manually, normalize with .split_whitespace().collect::<Vec<_>>().join(" "), then assert .contains(...) against the normalized string instead of calling expect(locator).to_contain_text(...) directly.
Proposed Solution
Add a small whitespace-normalization helper (collapse consecutive whitespace to a single space, trim ends) and apply it to both the actual text pulled from the DOM and the expected argument in to_have_text and to_contain_text (string-argument variants only; leave to_have_text_regex/to_contain_text_regex matching raw text). Add a test using a fixture element with multi-line/irregularly-spaced text content to lock in the behavior and prevent regression.
Ideal Rust API (internal change, no public API change needed):
Discovered in: Folio's browser preview E2E test suite (crates/folio-cli/tests/browser_preview_test.rs), while asserting on .workflow-progress text content rendered across multiple lines.
This issue was discovered during active development of Folio, a family media management system serving as a proving ground for playwright-rust.
Summary
expect(locator).to_contain_text(...)andto_have_text(...)compare rawinner_text()output with only outer whitespace trimmed, then do a plain substring/equality check. Upstream Playwright normalizes internal whitespace (collapsing runs of whitespace, including newlines, into single spaces) before matchingtoContainText/toHaveText, so elements that render text across multiple lines still match a single-line expectation. playwright-rust's assertions don't do this, causing spurious failures (and a full retry-timeout wait) on any element whose text content spans multiple lines or has irregular internal spacing.Discovered in: Folio (github-padamson/folio), browser preview E2E testing.
Current Behavior
In
crates/playwright/src/assertions.rsonmain(as of 2026-07-18),to_contain_text(lines 302-340) does:to_have_text(lines 210-250) does the same thing with equality instead ofcontains, and additionally only trims the expected string's outer whitespace (expected.trim()), not its internal whitespace. Neither method collapses internal whitespace/newlines inactualorexpected.Code that triggered this (from Folio's
crates/folio-cli/tests/browser_preview_test.rs):Error/Output:
The element's text content is semantically identical once whitespace is normalized, but the assertion burns the full 5s retry/timeout window before failing with a confusing error.
Expected Behavior
Whitespace (newlines, tabs, runs of spaces) should be collapsed to single spaces in both the actual DOM text and the expected string before the string-argument comparison in
to_have_textandto_contain_text, matching upstream Playwright's documented behavior.Upstream playwright behavior (JS):
Note: the regex variants (
to_have_text_regex,to_contain_text_regex) should not be changed — upstream matches regex patterns against the raw (non-normalized) text, so this normalization should only apply to the plain-string overloads.Impact on Folio
crates/folio-cli/tests/browser_preview_test.rs)text_content()manually, normalize with.split_whitespace().collect::<Vec<_>>().join(" "), then assert.contains(...)against the normalized string instead of callingexpect(locator).to_contain_text(...)directly.Proposed Solution
Add a small whitespace-normalization helper (collapse consecutive whitespace to a single space, trim ends) and apply it to both the actual text pulled from the DOM and the
expectedargument into_have_textandto_contain_text(string-argument variants only; leaveto_have_text_regex/to_contain_text_regexmatching raw text). Add a test using a fixture element with multi-line/irregularly-spaced text content to lock in the behavior and prevent regression.Ideal Rust API (internal change, no public API change needed):
Additional Context
mainbranch (git dependency,0.15.0-unreleased line per Folio'sCargo.tomlcomment), fetched/verified 2026-07-18to_have_attribute,to_have_class,to_have_css,to_have_count)Folio Context
Discovered in: Folio's browser preview E2E test suite (
crates/folio-cli/tests/browser_preview_test.rs), while asserting on.workflow-progresstext content rendered across multiple lines.This issue was discovered during active development of Folio, a family media management system serving as a proving ground for playwright-rust.
Labels:
folio-driven,bug