Module: RSpec::Signal::Symptoms::Selector

Defined in:
lib/rspec/signal/symptoms/selector.rb

Overview

A DOM node or piece of page text Capybara could not find.

The same selector missing from four pages is one missing partial, one renamed data-testid, one component that never mounted. Capybara says so two different ways depending on whether you used find or have_css, and those two produce different exception classes and different messages, so they are correctly different signatures -- but they are obviously the same symptom.

The selector itself is the cluster key, compared exactly. Two different selectors never meet.

Constant Summary collapse

TYPES =
"css|xpath|field|link or button|link|button|select box|checkbox|" \
"radio button|file field|fillable field|element|selector|table"
TARGET =

The selector as Capybara printed it: inspected, or a bare token.

/"(?:[^"\\]|\\.)*"|\S+/.freeze
VISIBILITY =
"(?:visible |invisible )?"
UNABLE =

find(...) and friends raise Capybara::ElementNotFound.

/Unable to find #{VISIBILITY}(?<type>#{TYPES})\s+(?<target>#{TARGET})/i.freeze
NO_MATCHES =

expect(page).to have_css(...) fails the matcher instead, with a different class and a different sentence for the same missing node.

Regexp.new("expected to find #{VISIBILITY}(?<type>#{TYPES})\\s+(?<target>#{TARGET})" \
".{0,200}?but there were no matches", Regexp::IGNORECASE)
TEXT =

Page text is not a selector, but it goes missing for the same reasons.

/(?:Unable to find|expected to find) text (?<target>#{TARGET})/i.freeze

Class Method Summary collapse

Class Method Details

.call(_failure, text) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rspec/signal/symptoms/selector.rb', line 35

def call(_failure, text)
  match = NO_MATCHES.match(text) || UNABLE.match(text)
  return missing_text(text) unless match

  target = unquote(match[:target])
  return nil if target.empty?

  type = match[:type].to_s.downcase
  Symptom.new(kind: :selector, key: "selector:#{type}:#{target}",
              label: "missing #{type} selector `#{safe(target)}`",
              detail: "no match for #{type} #{target}")
end

.missing_text(text) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rspec/signal/symptoms/selector.rb', line 48

def missing_text(text)
  match = TEXT.match(text)
  return nil unless match

  target = unquote(match[:target])
  return nil if target.empty?

  Symptom.new(kind: :missing_text, key: "text:#{target}",
              label: "missing page text `#{safe(target)}`",
              detail: "page did not contain \"#{target}\"")
end

.safe(target) ⇒ Object



68
69
70
# File 'lib/rspec/signal/symptoms/selector.rb', line 68

def safe(target)
  target.tr("`", "'")
end

.unquote(raw) ⇒ Object

Capybara prints the selector with inspect, so a [data-testid="x"] arrives with its inner quotes escaped.



62
63
64
65
66
# File 'lib/rspec/signal/symptoms/selector.rb', line 62

def unquote(raw)
  text = raw.to_s
  text = text[1..-2].to_s.gsub('\\"', '"').gsub("\\\\", "\\") if text.start_with?('"')
  text.gsub(/\s+/, " ").strip
end