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

  1. Compare like-for-like computed values: use 'rgb(...)' strings or Regex values ('font-size' => /px$/)
  2. Give transitions time: assert_matches_style('opacity' => '1', wait: 2)
  3. Assert on long-lived end-state styles only, not mid-animation values; disable animations in the test env when possible
  4. 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

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


AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21). Data as JSON: /api/errors/dcd7749319652d8d. Report an issue: GitHub.