Spec-Zone.ru › Ruby on Rails 4.1

модуль ActionDispatch::Assertions::SelectorAssertions

Добавляет метод assert_select для использования в функциональных тестах Rails, который может быть использован для утверждений о HTML-ответе действия контроллера. Вы также можете вызвать assert_select внутри другого assert_select, чтобы сделать утверждения об элементах, выбранных вложенным утверждением.

Используйте css_select для выбора элементов без утверждений, как из ответа HTML, так и из элементов, выбранных вложенным утверждением.

В дополнение к ответам в формате HTML вы можете сделать следующие утверждения:

  • assert_select_encoded — Утверждения на HTML, закодированном внутри XML, например, для работы с описаниями элементов ленты.

  • assert_select_email — Утверждения на теле HTML-письма.

Также см. HTML::Selector, чтобы узнать, как использовать селекторы.

Публичные методы экземпляра

assert_select(*args) { |matches| ... } Показать исходный код

Утверждение, которое выбирает элементы и выполняет одно или несколько проверок равенства.

Если первый аргумент — элемент, выбирает все соответствующие элементы, начиная с (и включая) этот элемент и все его дочерние элементы в порядке обхода в глубину.

Если элемент не указан, вызов assert_select выбирает из HTML-ответа, если assert_select не вызывается внутри блока assert_select.

При вызове с блоком assert_select передаёт массив выбранных элементов в блок. Вызов assert_select из блока без указания элемента выполняет утверждение по всему набору элементов, выбранных вложенным утверждением. В качестве альтернативы, массив можно итерировать, чтобы assert_select можно было вызвать отдельно для каждого элемента.

Пример

Если ответ содержит два упорядоченных списка, каждый из которых содержит четыре элемента списка, то:

assert_select "ol" do |elements|
  elements.each do |element|
    assert_select element, "li", 4
  end
end

будет выполнено, так же как и:

assert_select "ol" do
  assert_select "li", 8
end

Селектор может быть выражением селектора CSS (строка), выражением со значениями подстановки или объектом HTML::Selector.

Проверки равенства

Проверка равенства может быть одной из следующих:

  • true — Утверждение истинно, если выбрано хотя бы один элемент.

  • false — Утверждение истинно, если не выбрано ни одного элемента.

  • String/Regexp — Утверждение истинно, если текстовое значение хотя бы одного элемента соответствует строке или регулярному выражению.

  • Integer — Утверждение истинно, если выбрано ровно указанное количество элементов.

  • Range — Утверждение истинно, если количество выбранных элементов соответствует указанному диапазону.

Если проверка равенства не указана, утверждение истинно, если выбрано хотя бы один элемент.

Для выполнения нескольких проверок равенства используйте хеш со следующими ключами:

  • :text — Ограничить выбор элементами, имеющими это текстовое значение (строка или регулярное выражение).

  • :html — Ограничить выбор элементами, содержащими это содержимое HTML (строка или регулярное выражение).

  • :count — Утверждение истинно, если количество выбранных элементов равно этому значению.

  • :minimum — Утверждение истинно, если количество выбранных элементов не меньше этого значения.

  • :maximum — Утверждение истинно, если количество выбранных элементов не больше этого значения.

Если метод вызывается с блоком, после оценки всех проверок равенства вызывается блок с массивом всех совпавших элементов.

# At least one form element
assert_select "form"

# Form element includes four input fields
assert_select "form input", 4

# Page title is "Welcome"
assert_select "title", "Welcome"

# Page title is "Welcome" and there is only one title element
assert_select "title", {count: 1, text: "Welcome"},
    "Wrong title or more than one title element"

# Page contains no forms
assert_select "form", false, "This page must contain no forms"

# Test the content and style
assert_select "body div.header ul.menu"

# Use substitution values
assert_select "ol>li#?", /item-\d+/

# All input fields in the form have a name
assert_select "form input" do
  assert_select "[name=?]", /.+/  # Not empty
end
# File actionpack/lib/action_dispatch/testing/assertions/selector.rb, line 184
def assert_select(*args, &block)
  # Start with optional element followed by mandatory selector.
  arg = args.shift
  @selected ||= nil

  if arg.is_a?(HTML::Node)
    # First argument is a node (tag or text, but also HTML root),
    # so we know what we're selecting from.
    root = arg
    arg = args.shift
  elsif arg == nil
    # This usually happens when passing a node/element that
    # happens to be nil.
    raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
  elsif @selected
    root = HTML::Node.new(nil)
    root.children.concat @selected
  else
    # Otherwise just operate on the response document.
    root = response_from_page
  end

  # First or second argument is the selector: string and we pass
  # all remaining arguments. Array and we pass the argument. Also
  # accepts selector itself.
  case arg
    when String
      selector = HTML::Selector.new(arg, args)
    when Array
      selector = HTML::Selector.new(*arg)
    when HTML::Selector
      selector = arg
    else raise ArgumentError, "Expecting a selector as the first argument"
  end

  # Next argument is used for equality tests.
  equals = {}
  case arg = args.shift
    when Hash
      equals = arg
    when String, Regexp
      equals[:text] = arg
    when Integer
      equals[:count] = arg
    when Range
      equals[:minimum] = arg.begin
      equals[:maximum] = arg.end
    when FalseClass
      equals[:count] = 0
    when NilClass, TrueClass
      equals[:minimum] = 1
    else raise ArgumentError, "I don't understand what you're trying to match"
  end

  # By default we're looking for at least one match.
  if equals[:count]
    equals[:minimum] = equals[:maximum] = equals[:count]
  else
    equals[:minimum] = 1 unless equals[:minimum]
  end

  # Last argument is the message we use if the assertion fails.
  message = args.shift
  #- message = "No match made with selector #{selector.inspect}" unless message
  if args.shift
    raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
  end

  matches = selector.select(root)
  # If text/html, narrow down to those elements that match it.
  content_mismatch = nil
  if match_with = equals[:text]
    matches.delete_if do |match|
      text = ""
      stack = match.children.reverse
      while node = stack.pop
        if node.tag?
          stack.concat node.children.reverse
        else
          content = node.content
          text << content
        end
      end
      text.strip! unless NO_STRIP.include?(match.name)
      text.sub!(/\A\n/, '') if match.name == "textarea"
      unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
        content_mismatch ||= sprintf("<%s> expected but was\n<%s>", match_with, text)
        true
      end
    end
  elsif match_with = equals[:html]
    matches.delete_if do |match|
      html = match.children.map(&:to_s).join
      html.strip! unless NO_STRIP.include?(match.name)
      unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
        content_mismatch ||= sprintf("<%s> expected but was\n<%s>", match_with, html)
        true
      end
    end
  end
  # Expecting foo found bar element only if found zero, not if
  # found one but expecting two.
  message ||= content_mismatch if matches.empty?
  # Test minimum/maximum occurrence.
  min, max, count = equals[:minimum], equals[:maximum], equals[:count]

  # FIXME: minitest provides messaging when we use assert_operator,
  # so is this custom message really needed?
  message = message || %Q(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size})
  if count
    assert_equal count, matches.size, message
  else
    assert_operator matches.size, :>=, min, message if min
    assert_operator matches.size, :<=, max, message if max
  end

  # If a block is given call that block. Set @selected to allow
  # nested assert_select, which can be nested several levels deep.
  if block_given? && !matches.empty?
    begin
      in_scope, @selected = @selected, matches
      yield matches
    ensure
      @selected = in_scope
    end
  end

  # Returns all matches elements.
  matches
end
assert_select_email(&block) Показать исходный код

Извлекает тело письма и выполняет вложенные утверждения на нём.

Для работы этого утверждения необходимо включить доставку, используйте:

 ActionMailer::Base.perform_deliveries = true

assert_select_email do
  assert_select "h1", "Email alert"
end

assert_select_email do
  items = assert_select "ol>li"
  items.each do
     # Work with items here...
  end
end
# File actionpack/lib/action_dispatch/testing/assertions/selector.rb, line 409
def assert_select_email(&block)
  deliveries = ActionMailer::Base.deliveries
  assert !deliveries.empty?, "No e-mail in delivery list"

  deliveries.each do |delivery|
    (delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
      if part["Content-Type"].to_s =~ /^text\/html\W/
        root = HTML::Document.new(part.body.to_s).root
        assert_select root, ":root", &block
      end
    end
  end
end
assert_select_encoded(element = nil, &block) Показать исходный код

Извлекает содержимое элемента, обрабатывает его как закодированный HTML и выполняет на нём вложенное утверждение.

Обычно этот метод вызывается внутри другого утверждения для работы со всеми текущими выбранными элементами. Вы также можете передать элемент или массив элементов.

Содержимое каждого элемента раскодируется и оборачивается в корневой элемент encoded. Затем вызывается блок со всеми раскодированными элементами.

# Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
  # Select each entry item and then the title item
  assert_select "entry>title" do
    # Run assertions on the encoded title elements
    assert_select_encoded do
      assert_select "b"
    end
  end
end

# Selects all paragraph tags from within the description of an RSS feed
assert_select "rss[version=2.0]" do
  # Select description element of each feed item.
  assert_select "channel>item>description" do
    # Run assertions on the encoded elements.
    assert_select_encoded do
      assert_select "p"
    end
  end
end
# File actionpack/lib/action_dispatch/testing/assertions/selector.rb, line 361
def assert_select_encoded(element = nil, &block)
  case element
    when Array
      elements = element
    when HTML::Node
      elements = [element]
    when nil
      unless elements = @selected
        raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
      end
    else
      raise ArgumentError, "Argument is optional, and may be node or array of nodes"
  end

  fix_content = lambda do |node|
    # Gets around a bug in the Rails 1.1 HTML parser.
    node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
  end

  selected = elements.map do |elem|
    text = elem.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
    root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
    css_select(root, "encoded:root", &block)[0]
  end

  begin
    old_selected, @selected = @selected, selected
    assert_select ":root", &block
  ensure
    @selected = old_selected
  end
end
css_select(*args) Показать исходный код

Выбирает и возвращает все соответствующие элементы.

Если вызвана с одним аргументом, использует этот аргумент в качестве селектора для сопоставления всех элементов текущей страницы. Возвращает пустой массив, если совпадений не найдено.

Если вызвана с двумя аргументами, использует первый аргумент в качестве базового элемента, а второй — в качестве селектора. Попытка сопоставления с базовым элементом и любыми его дочерними элементами. Возвращает пустой массив, если совпадений не найдено.

Селектор может быть выражением селектора CSS (строка), выражением со значениями подстановки (массив) или объектом HTML::Selector.

# Selects all div tags
divs = css_select("div")

# Selects all paragraph tags and does something interesting
pars = css_select("p")
pars.each do |par|
  # Do something fun with paragraphs here...
end

# Selects all list items in unordered lists
items = css_select("ul>li")

# Selects all form tags and then all inputs inside the form
forms = css_select("form")
forms.each do |form|
  inputs = css_select(form, "input")
  ...
end
# File actionpack/lib/action_dispatch/testing/assertions/selector.rb, line 60
def css_select(*args)
  # See assert_select to understand what's going on here.
  arg = args.shift

  if arg.is_a?(HTML::Node)
    root = arg
    arg = args.shift
  elsif arg == nil
    raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
  elsif defined?(@selected) && @selected
    matches = []

    @selected.each do |selected|
      subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
      subset.each do |match|
        matches << match unless matches.any? { |m| m.equal?(match) }
      end
    end

    return matches
  else
    root = response_from_page
  end

  case arg
    when String
      selector = HTML::Selector.new(arg, args)
    when Array
      selector = HTML::Selector.new(*arg)
    when HTML::Selector
      selector = arg
    else raise ArgumentError, "Expecting a selector as the first argument"
  end

  selector.select(root)
end

Защищённые методы экземпляра

response_from_page() Показать исходный код

assert_select и css_select вызывают это для получения содержимого страницы HTML.

# File actionpack/lib/action_dispatch/testing/assertions/selector.rb, line 425
def response_from_page
  html_document.root
end

© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API