teamcapybara/capybara · error · Capybara::ExpectationNotMet

Item matched the provided selector

Error message

Item matched the provided selector

What it means

assert_not_matches_selector is the inverse of assert_matches_selector: it raises ExpectationNotMet 'Item matched the provided selector' when the receiver element DOES match the selector plus filters, which the caller expected it not to. Same MatchQuery machinery (_verify_match_result), inverted condition on result.include?(self).

Source

Thrown at lib/capybara/node/matchers.rb:604

      # @raise [Capybara::ExpectationNotMet]      If the selector does not match
      #
      def assert_matches_selector(*args, &optional_filter_block)
        _verify_match_result(args, optional_filter_block) do |result|
          raise Capybara::ExpectationNotMet, 'Item does not match the provided selector' unless result.include? self
        end
      end

      ##
      #
      # Asserts that the current node does not match a given selector.
      # Usage is identical to {#assert_matches_selector}.
      #
      # @param (see #assert_matches_selector)
      # @raise [Capybara::ExpectationNotMet]      If the selector matches
      #
      def assert_not_matches_selector(*args, &optional_filter_block)
        _verify_match_result(args, optional_filter_block) do |result|
          raise Capybara::ExpectationNotMet, 'Item matched the provided selector' if result.include? self
        end
      end

      ##
      #
      # Checks if the current node matches given selector.
      #
      # @param (see #has_selector?)
      # @return [Boolean]
      #
      def matches_selector?(*args, **options, &optional_filter_block)
        make_predicate(options) { assert_matches_selector(*args, options, &optional_filter_block) }
      end

      ##
      #
      # Checks if the current node matches given XPath expression.
      #

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Check the element's actual state (el[:class], el.checked?) and adjust the expectation or drive the UI to change it first
  2. Add wait for state transitions: assert_not_matches_selector('li', '.active', wait: 3) — note match queries reject count options, not wait
  3. Use exact text if partial matching causes false positives: assert_not_matches_selector('a', text: 'Delete', exact_text: true)
  4. Confirm you are asserting on the intended element instance (re-find after DOM replacement)

Example fix

# before
row.assert_not_matches_selector('tr', '.selected')  # still selected

# after
click_link('Unselect')
row.assert_not_matches_selector('tr', '.selected', wait: 3)
Defensive patterns

Strategy: validation

Validate before calling

return if el.matches_selector?('li', '.active') # nothing to assert
el.assert_not_matches_selector('li', '.active')

Type guard

def node_free_of?(node, *args, **opts, &block)
  node.not_matches_selector?(*args, **opts, &block)
end

Try / catch

begin
  el.assert_not_matches_selector('a', text: 'Delete', exact_text: true, wait: 3)
rescue Capybara::ExpectationNotMet
  # node unexpectedly matches — drive UI to clear state, then re-check once
  el.assert_not_matches_selector('a', text: 'Delete', exact_text: true, wait: 5)
end

Prevention

When it happens

Trigger: el.assert_not_matches_selector('li') on an element that is an li; assert_not_matches_selector('a', text: 'Delete') where the text matches exactly; asserting not-checked with assert_not_matches_selector('input', checked: true) while the checkbox is indeed checked; verifying an element lost a class ('.active') while the class is still applied.

Common situations: Guard assertions that a node no longer carries a state (active/disabled/selected) that in fact persists, copy changes making text filters match again, class-removal transitions not yet applied at assert time.

Related errors


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