Differences between assert_tag and assert_select

Hi all,

I can't seem to make assert_select work for the more complex cases for me.

Here's a sample:

# View
<%= link_to_remote 'Add new', :url => new_phone_url, :submit => 'phones_head' %>

# Generated code:
        <a href="#" onclick=“new
Ajax.Request(‘http://test.host/admin/parties/phone/new’,
{asynchronous:true, evalScripts:true,
parameters:Form.serialize(‘phones_head’)}); return false;”>Add new</a>

# Succeeds:
assert_tag :a, :attributes => {:onclick =>
Regexp.new(Regexp.escape(new_phone_url))}, :content => /add new/i

# Fails
assert_select "a[onclick=?]",
Regexp.new(Regexp.escape(new_phone_url)), /add new/i

test_link_to_add_address_exists(NewPartyViewTest)
    [/home/francois/src/config/../vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb:281:in
`assert_select'
     test/functional/admin/parties_controller_test.rb:1518:in
`test_link_to_add_address_exists']:
Expected at least 1 elements, found 0.
<false> is not true.

There's probably something I'm not doing right, for sure.
#assert_select is supposed to be so much easier to use, but I'm not
find that at all. I just learned about "a[onclick*=?]", and I can
make my test pass with that. But regexps sure are something I use
very often.

Thanks !

François,

You probably don't need to be this exact to test the accuracy of your
view rendering. To make things easier you might want to just add a
class to your anchor.

assert_select 'a[class=my-link-class]', /Add new/i

Hope this helps.

Hello Zack,

# View
<%= link_to_remote 'Add new', :url => new_phone_url, :submit => 'phones_head' %>

# Generated code:
        <a href="#" onclick=“new
Ajax.Request(‘http://test.host/admin/parties/phone/new’,
{asynchronous:true, evalScripts:true,
parameters:Form.serialize(‘phones_head’)}); return false;”>Add new</a>

# Succeeds:
assert_tag :a, :attributes => {:onclick =>
Regexp.new(Regexp.escape(new_phone_url))}, :content => /add new/i

# Fails
assert_select "a[onclick=?]",
Regexp.new(Regexp.escape(new_phone_url)), /add new/i

I don't think you can pass a regular expression to your attribute selector, i.e. in a[onclick=XXX], XXX must be a string. Therefore I think you have to express the test for the attribute value and the test for the link's text in two separate selector statements.

Using a substitution value as you have done allows you to test the attribute's value but precludes testing the link's text in the same line (I think).

This might do it:

assert_select "a[onclick]", /add new/i
assert_select "a[onclick=?]", Regexp.new(Regexp.escape(new_phone_url))

Hope that helps somehow.

Regards,
Andy Stewart

Hello Andy,

I don't think you can pass a regular expression to your attribute
selector, i.e. in a[onclick=XXX], XXX must be a string. Therefore I
think you have to express the test for the attribute value and the
test for the link's text in two separate selector statements.

http://labnotes.org/svn/public/ruby/rails_plugins/assert_select/cheat/assert_select.html

Look at the "Substitution Values" section.

assert_select "a[onclick]", /add new/i
assert_select "a[onclick=?]", Regexp.new(Regexp.escape(new_phone_url))

There's nothing that confirms that the same node was used here. It
might be an entirely different one. The form on which this is checked
has the capability of adding phones, addresses, tesimonials, invoices,
payments and other objects too. And "Add new" is the standard text to
add a new something.

Thanks for the reply !

How would I turn the following into using assert_select?

assert_tag :tag => /input|textarea|select/, :attributes => { :name => /#{obj}[#{field_name}]|#{obj}[#{field_name}(\di)]/ }

May it’s obvious and assert_select will take a regex like this but I couldn’t find any info. Thanks.

Jason

Hello Francis,

assert_select "a[onclick]", /add new/i
assert_select "a[onclick=?]", Regexp.new(Regexp.escape(new_phone_url))

Try this (it works for me):

   assert_select "a[onclick=?]",
                 Regexp.new(".+#{Regexp.escape(new_phone_url)}.+")),
                 :text => /add new/i

The substitution value consumes the next argument, the regular expression, and one can test the text as usual.

The trick seems to be -- at least on my system -- that the regexp substitution value does not behave as one would expect. It appears to assume anchors to the start and end of the attribute value even if one doesn't put in those anchors.

For example, your onclick attribute value is a long AJAXy string and you just want to match the new_phone_url somewhere inside it. Let's say that URL is: /phones/123/new'

I originally assumed that this would be the correct regexp to use: /\/phones\/\d+\/new/

But in my experiments I found assert_select behaves as if I had used: /^\/phones\/\d+\/new$/

So I added .+ to the start and end of my regexp to allow for the AJAXy stuff between the start of the onlick attribute's value and the end, giving:

/.+\/phones\/\d+\/new.+/

Does that make sense? And if it does, does it work for you? :slight_smile:

Regards,
Andy Stewart

Hello Andrew,

So I added .+ to the start and end of my regexp to allow for the
AJAXy stuff between the start of the onlick attribute's value and the
end, giving:

/.+\/phones\/\d+\/new.+/

Does that make sense? And if it does, does it work for you? :slight_smile:

It makes perfect sense, and I changed only one thing from your
solution. I used .* instead of .+, to allow for the fact that the
string might be anywhere.

Looking at Rails code, I can see this:

# actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
# Lines 682 and later (Rails 1.2.1)

def attribute_match(equality, value)
  regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
  case equality
    when "=" then
      # Match the attribute value in full
      Regexp.new("^#{regexp}$")

# ^^^ see this ?

# ...
  end
end

So, using "a[onclick*=?]", Regexp is the right solution, then.

Thanks for your digging around.

Bye !