teamcapybara/capybara · error · Capybara::ExpectationNotMet
query.failure_message
Error message
query.failure_message
What it means
assert_matches_style builds a Capybara::Queries::StyleQuery and raises ExpectationNotMet with query.failure_message when the element's computed styles do not include the requested style values within the wait time. The query re-evaluates computed styles on each retry inside synchronize, so transient differences (CSS transitions, late stylesheet application) resolve themselves if they finish before the deadline.
Source
Thrown at lib/capybara/node/matchers.rb:131
end
end
end
##
#
# Asserts that an element has the specified CSS styles.
#
# element.assert_matches_style( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
#
# @param styles [Hash]
# @raise [Capybara::ExpectationNotMet] If the element doesn't have the specified styles
#
def assert_matches_style(styles = nil, **options)
styles, options = options, {} if styles.nil?
query_args, query_opts = _set_query_session_options(styles, options)
query = Capybara::Queries::StyleQuery.new(*query_args, **query_opts)
synchronize(query.wait) do
raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
end
true
end
##
# @deprecated Use {#assert_matches_style} instead.
#
def assert_style(styles = nil, **options)
warn 'assert_style is deprecated, please use assert_matches_style instead'
assert_matches_style(styles, **options)
end
# Asserts that all of the provided selectors are present on the given page
# or descendants of the current node. If options are provided, the assertion
# will check that each locator is present with those options as well (other than `:wait`).
#
# page.assert_all_of_selectors(:custom, 'Tom', 'Joe', visible: all)
# page.assert_all_of_selectors(:css, '#my_div', 'a.not_clicked')View on GitHub (pinned to 15b5fdb76e)
Solutions
- Compare like-for-like computed values: use 'rgb(...)' strings or Regex values ('font-size' => /px$/)
- Give transitions time: assert_matches_style('opacity' => '1', wait: 2)
- Assert on long-lived end-state styles only, not mid-animation values; disable animations in the test env when possible
- Check the element actually receives the CSS class/rule first (assert_selector on the class) before asserting styles
Example fix
# before
el.assert_matches_style('opacity' => '1') # fires mid fade-in
# after
el.assert_matches_style('opacity' => '1', wait: 3)
# or match loosely
el.assert_matches_style('font-size' => /px/) Defensive patterns
Strategy: retry
Validate before calling
transitioning = %w[transition animation].any? { |p| el[:style].to_s.include?(p) }
skip unless transitioning # or add wait below Try / catch
begin
el.assert_matches_style('opacity' => '1', wait: 3)
rescue Capybara::ExpectationNotMet => e
puts el.evaluate_script('getComputedStyle(arguments[0]).opacity')
raise
end Prevention
- Assert end-state styles with wait: >= transition duration
- Use regex values for values browsers normalize differently (px, rgb)
- Disable CSS animations in the test environment where possible
When it happens
Trigger: element.assert_matches_style('color' => 'rgb(0,0,255)') when the computed color is 'rgb(0,0,254)' or a different color function; asserting 'opacity' => '1' immediately after triggering a fade-in transition; regex value '/px/' against a font-size computed in rem/pt; styles applied by a stylesheet that loads after first assert.
Common situations: Animations/transitions making computed values time-dependent (assert fires mid-transition), browser-dependent computed-value normalization (colors as rgb vs rgba vs color(), font shorthand expansion), asserting shorthand properties that browsers do not report as set, headless driver default styles differing from desktop.
Related errors
- result.failure_message
- res.join(' or ')
- result.negative_failure_message
- Item does not match the provided selector
- Item matched the provided selector
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/dcd7749319652d8d.
Report an issue: GitHub.