teamcapybara/capybara · error · Capybara::ExpectationNotMet
query.negative_failure_message
Error message
query.negative_failure_message
What it means
assert_no_text raises ExpectationNotMet with query.negative_failure_message ('expected not to find text ...') when the text still occurs (and its count matches the count options) after the wait time. It is the negated TextQuery assertion: any lingering occurrence of the string/regexp is a failure.
Source
Thrown at lib/capybara/node/matchers.rb:721
_verify_text(type_or_text, *args, **opts) do |count, query|
unless query.matches_count?(count) && (count.positive? || query.expects_none?)
raise Capybara::ExpectationNotMet, query.failure_message
end
end
end
##
# Asserts that the page or current node doesn't have the given text content,
# ignoring any HTML tags.
#
# @macro text_query_params
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
# @return [true]
#
def assert_no_text(type_or_text, *args, **opts)
_verify_text(type_or_text, *args, **opts) do |count, query|
if query.matches_count?(count) && (count.positive? || query.expects_none?)
raise Capybara::ExpectationNotMet, query.negative_failure_message
end
end
end
##
# Checks if the page or current node has the given text content,
# ignoring any HTML tags.
#
# By default it will check if the text occurs at least once,
# but a different number can be specified.
#
# page.has_text?('lorem ipsum', between: 2..4)
#
# This will check if the text occurs from 2 to 4 times.
#
# @macro text_query_params
# @return [Boolean] Whether it exists
#View on GitHub (pinned to 15b5fdb76e)
Solutions
- Wait out the removal: assert_no_text('Loading', wait: 10)
- Scope the assertion to the region where the text must vanish (within('#main') { assert_no_text('Loading') })
- Make the regexp precise so legitimate copy does not match (/\bError:/ instead of /error/i)
- If the text is hidden but present and that is acceptable, assert on a visible selector instead of raw text
Example fix
# before
page.assert_no_text('Loading')
# after
page.assert_no_text('Loading', wait: 10)
# or scope where it must disappear
within('#content') { page.assert_no_text('Loading', wait: 10) } Defensive patterns
Strategy: validation
Validate before calling
return unless page.has_text?('Loading')
page.assert_no_text('Loading', wait: 10) # only assert while removal is expected Type guard
def text_gone?(page, text, wait: 5) !page.has_text?(text, wait: wait) end
Try / catch
begin
page.assert_no_text('Loading', wait: 10)
rescue Capybara::ExpectationNotMet => e
puts e.message # shows where the text still occurs
raise
end Prevention
- Scope assert_no_text to the container where the text must disappear
- Make regexes precise so legitimate copy does not match
- Remember TextQuery sees hidden text too — assert visible selectors if that matters
When it happens
Trigger: page.assert_no_text('Loading') while a spinner with that text is still visible; assert_no_text('Error') after a form submit that re-rendered an error flash; regexp variants matching unexpectedly (/error/i matching 'No errors'); asserting too early while fade-out of the message is still running.
Common situations: Spinners/loading text driven by JS that outlives the wait, flash messages persisted across requests in the session, copy appearing in hidden elements (TextQuery sees text content regardless of visibility), negative assertions in shared examples hitting pages that legitimately contain the string.
Related errors
- result.negative_failure_message
- Item matched the provided selector
- result.failure_message
- Unable to find #{query.applied_description}
- result.failure_message
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/e1b1ec884a3be2b8.
Report an issue: GitHub.