Forum: RSpec How To Drive Out AJAX Functionality in a Rails View

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
05748a61a1b05130ec508434301aecf1?d=identicon&s=25 Lee Longmore (Guest)
on 2009-05-12 15:01
(Received via mailing list)
I am new to RSpec and have just started to drive out my first Rails view
using a spec. I have used the The RSpec Book (beta) to do the basic
stuff like testing for the presence of a field but I am unsure where to
start for driving out some AJAX functionality.

In the view, I will have a text field that a user can enter a name into
and then click on a link to check the availability of this name or
otherwise. I plan to implement the check as an AJAX request, returning
an HTML snippet or 'available' or 'not available'.

I would appreciate some help on what sort of examples one might write
and the various mocks, stubs and helpers etc that can be used. Or
alternatively a pointer to some existing information/tutorials on this
topic.

Thanks.



mobile: +44(0)775 392 8067
home: +44(0) 208 8358256
email:   lee_longmore@yahoo.co.uk
Aafa8848c4b764f080b1b31a51eab73d?d=identicon&s=25 Phlip (Guest)
on 2009-05-12 15:37
(Received via mailing list)
Lee Longmore wrote:
> I am new to RSpec and have just started to drive out my first Rails view
> using a spec. I have used the The RSpec Book (beta) to do the basic
> stuff like testing for the presence of a field but I am unsure where to
> start for driving out some AJAX functionality.
>
> In the view, I will have a text field that a user can enter a name into
> and then click on a link to check the availability of this name or
> otherwise. I plan to implement the check as an AJAX request, returning
> an HTML snippet or 'available' or 'not available'.

gem install nokogiri rkelly assert2
...
require 'assert2/rjs'

   specify 'a simple passing assertion works' do
     xhr :get, :xhr_availability, :name => 'masone'
     js = @response.body
     js.should send_js_to(:replace_html, 'label_7', /available/)

     js.should send_js_to(:replace_html, 'label_7'){
                 span /available/
                 }
   end

The first send_js_to just checks for a regexp to match the payload of
the
Element.update call. The second one uses {} to generate a Nokogiri
Builder
snippet of HTML, and match that. Here it's just span, but it could have
been
more complex HTML. Google assert_xhtml for more on that.

> I would appreciate some help on what sort of examples one might write
> and the various mocks, stubs and helpers etc that can be used. Or
> alternatively a pointer to some existing information/tutorials on this
> topic.

The RSpec community, in my exalted opinion, mocks too much! If you can't
use the
real thing, it's too coupled, so break it up!

--
   Phlip
   http://flea.sourceforge.net/resume.html
Aafa8848c4b764f080b1b31a51eab73d?d=identicon&s=25 Phlip (Guest)
on 2009-05-12 15:58
(Received via mailing list)
Lee Longmore wrote:

> I am new to RSpec and have just started to drive out my first Rails view
> using a spec. I have used the The RSpec Book (beta) to do the basic
> stuff like testing for the presence of a field but I am unsure where to
> start for driving out some AJAX functionality.

To test the onchange='' of the field, start by pulling in the Test::Unit
versions of the assertions:

   Spec::Runner.configure do |c|
     c.include Test::Unit::Assertions
   end

Now use assert_xhtml to grab your edit field and return it as a Nokogiri
node.
This line does it for a <a href='#'>All</a>:

     a = assert_xhtml{|x|  x.a 'All', :href => '#'  }
     assert{ a[:onclick] =~ /new Ajax.Request.*xhr_run_all/ }

.send_js_to is supposed to work in the second line, but I have not yet
researched out how get rkelly to parse the Ajax.Request line.

The point of using rkelly inside send_js_to (aka assert_rjs_) is it's a
real
JavaScript lexer, not a Regexp, so when I get it working it will be very
accurate!

--
   Phlip
   http://flea.sourceforge.net/resume.html
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-05-12 16:12
(Received via mailing list)
On Tue, May 12, 2009 at 8:33 AM, Phlip <phlip2005@gmail.com> wrote:
>> HTML snippet or 'available' or 'not available'.
>    js.should send_js_to(:replace_html, 'label_7'){
>> the various mocks, stubs and helpers etc that can be used. Or alternatively
>> a pointer to some existing information/tutorials on this topic.
>
> The RSpec community, in my exalted opinion, mocks too much! If you can't use
> the real thing, it's too coupled, so break it up!

Phlip,

You could just say "be careful not to mock too much," which would be
really good advice, but instead you make a generalizing and judgmental
statement like this. When's the last time you paired with someone in
the RSpec community?

Lee,

Phlip has some really great ideas about some things, and assert2 is
lovely, but based on several posts on this and other lists, I'd say he
doesn't really like RSpec, nor understand its underlying intent. So by
all means listen to his advice, and by all means take advantage of
assert2 for this particular problem, but please also understand that
he is in no way the voice of the RSpec community.

Cheers,
David
4c63a3e984016307fc7dc418a9fd75f1?d=identicon&s=25 Mike Doel (Guest)
on 2009-05-12 16:28
(Received via mailing list)
On May 12, 2009, at 8:36 AM, Lee Longmore wrote:

> I am new to RSpec and have just started to drive out my first Rails
> view using a spec. I have used the The RSpec Book (beta) to do the
> basic stuff like testing for the presence of a field but I am unsure
> where to start for driving out some AJAX functionality.

Check out Adam McCrea's fork of webrat:

http://github.com/adamlogic/webrat/tree/master

It includes a submit_form_via_ajax method that I use to do this.  It
basically just forces Rails into treating the form request as asking
for a .js response.  There are limitations on what you can do with the
response (e.g. inspecting the entire page is no longer possible), but
you are able to look at the response body of the call and match it
against regular expressions.  So, I use features like:

     When I go to the homepage
     And I use a search term of "foo"
     And I press "search"
     And the edit_search_form is submitted via ajax
     Then the search results should have 2 bars

with the critical step like:

When /^the (.+) is submitted via ajax$/ do |form|
   submit_form_via_ajax form
end

I've left out a couple of things here (in particular, a step that
fills in form fields to mimic javascript action), but this basic idea
makes it possible to have acceptance tests for ajax based features
that are written in the language of the customer and yet operate
quickly (i.e. you don't need to have selenium or something else like
that involved to run this).

The drawback of course is that it's not really exercising the full end-
to-end system.  While I understand that this is frowned upon (and for
good reason), in my case, I have decided that the speed of the tests
(and thus my willingness to use them) makes up for the drawbacks
involved.  To limit my exposure, I'm working on including javascript
unit tests that will ensure that the behavior I stub out above
actually works as expected.

Mike Doel
753038ae16700c0fd9a5e1d2e80f68f6?d=identicon&s=25 Lee (Guest)
on 2009-05-12 16:34
(Received via mailing list)
Thanks Philip.

I should have added in my original post that I have included the
Webrat::Matchers as instructed in The Rspec Book.

Unless I have misunderstood you, it appears that RSpec with
Webrat::Matchers do not support the driving out of AJAX functionality
in views, hence the need to use Nokogiri. Is my understanding correct?
Or is this your preferred approach but there are others?
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-05-12 17:10
(Received via mailing list)
On 12 May 2009, at 15:07, David Chelimsky wrote:
> David
Lee,

One thing you might want to consider is looking at using a headless
browser like 'celerity' for testing your ajax code. This is quite a
bit more involved for initial setup than using RSpec but will give you
real confidence as you're actually running your javascript inside a
browser. There are quite a few people on this list who are doing this,
using the 'progressive enhancement'[1] pattern to build a version of
the page that works with raw HTML, then decorate it with javascript
behaviour when the page loads.

I'm not doing this myself in earnest, so I'll say no more, but
hopefully a couple of other people will chime in with some advice.

[1]http://en.wikipedia.org/wiki/Progressive_enhancement

Matt Wynne
http://blog.mattwynne.net
http://www.songkick.com
753038ae16700c0fd9a5e1d2e80f68f6?d=identicon&s=25 Lee (Guest)
on 2009-05-12 17:17
(Received via mailing list)
Thanks Mike.

fyi, I am trying out Cucumber and Culerity for integration testing.
Culerity provides for AJAX testing.

Having written my first Cucumber (with Culerity) feature and set of
scenarios, I was keen to then use RSpec to begin driving out the
views, controllers etc so that I can eventually run the e2e Cucumber
scenarios.
753038ae16700c0fd9a5e1d2e80f68f6?d=identicon&s=25 Lee (Guest)
on 2009-05-12 17:57
(Received via mailing list)
Thanks Matt.

I have been trying out Cucumber and Culerity (Celerity) but rather
than jump directly from Cucumber to coding the necessary views,
controllers and models required for each scenario, I wanted to drive
out the code using RSpec. So I've started to spec a view required by a
scenario, and this is where I am confused about how I approach the
AJAX aspects of the view in my specs.
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-05-12 18:03
(Received via mailing list)
On 12 May 2009, at 16:33, Lee wrote:

> Thanks Matt.
>
> I have been trying out Cucumber and Culerity (Celerity) but rather
> than jump directly from Cucumber to coding the necessary views,
> controllers and models required for each scenario, I wanted to drive
> out the code using RSpec. So I've started to spec a view required by a
> scenario, and this is where I am confused about how I approach the
> AJAX aspects of the view in my specs.

If you're using progressive enhancement though, I wouldn't expect the
views themselves to be any different - the code to add javascript
behaviour is all in external js files. I'd personally recommend doing
that over using the rails js generators that squirt behaviour into
your onclick events. eugh.

>> behaviour when the page loads.
>> rspec-us...@rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

Matt Wynne
http://blog.mattwynne.net
http://www.songkick.com
Aafa8848c4b764f080b1b31a51eab73d?d=identicon&s=25 Phlip (Guest)
on 2009-05-12 20:05
(Received via mailing list)
David Chelimsky wrote:

> You could just say "be careful not to mock too much," which would be
> really good advice,

Sorry; I thought I said that. This industry in general dislikes strong
opinions...

Mock on, everyone!

 > but instead you make a generalizing and judgmental
> statement like this. When's the last time you paired with someone in
> the RSpec community?

Well it's not for want of trying!

--
   Phlip
   http://flea.sourceforge.net/resume.html
753038ae16700c0fd9a5e1d2e80f68f6?d=identicon&s=25 Lee (Guest)
on 2009-05-13 09:52
(Received via mailing list)
I found a potential solution from this blog:
http://www.rubytutorials.net/2008/02/29/small-rspe...

In my spec for the view in which I want to include the AJAX
functionality ("new.html.erb_spec.erb"), I have added a couple of
Examples to drive out the some of the AJAX functionality:

  it "should render /Available/ if name does not exist" do
    @name_exists = false
    assigns[:name_exists] = @name_exists
    render "contexts/check.js.rjs"
    response.should have_rjs(:chained_replace_html, 'checkname') do
      response.should have_text(/Available/)
    end
  end

  it "should render /Not available/ if name does not exist" do
    @name_exists = true
    assigns[:name_exists] = @name_exists
    render "contexts/check.js.rjs"
    response.should have_rjs(:chained_replace_html, 'checkname') do
      response.should have_text(/Not available/)
    end
  end

The rendered template "check.js.rjs" contains:

if @name_exists
  page['checkname'].update('Not available')
else
 page['checkname'].update('Available')
end

These examples pass and seem to achieve my objective of using the spec
to drive out the required AJAX behaviour, or at least some of it.
Though I suspect that this approach may have limitations for other
types of AJAX functionality.

At this stage, I am wondering whether it is worth trying to drive out
any AJAX functionality at the point of writing RSpec view specs. Some
replies seem to suggest the use of Cucumber with Culerity/Webrat/
Selenium and I read into this that one would bypass view specs for the
AJAX related steps and just write the necessary view code (e.g.
JavaScript or Rails Prototype helpers) to get the step to pass.

Further thoughts appreciated.

Thanks Lee.
Ef0db53920b243d6758c2f6b1306df0d?d=identicon&s=25 Steve Ross (cwd)
on 2009-05-13 22:14
(Received via mailing list)
Hi--

On May 13, 2009, at 12:47 AM, Lee wrote:

> I found a potential solution from this blog:
> http://www.rubytutorials.net/2008/02/29/small-rspe...
>
> In my spec for the view in which I want to include the AJAX
> functionality ("new.html.erb_spec.erb"), I have added a couple of
> Examples to drive out the some of the AJAX functionality:
>
<snip>

The one thing I would add is that Rails responds differently when it
receives a post with XMLHttpHeaders than when these headers are not
present. I typically put this in my spec_helper.rb and then I can
include it where necessary.

# spec_helper.rb
module XmlHttpHelper
  def set_xhr_headers
    request.env['HTTP_ACCEPT'] = 'application/json, text/javascript, */
*'
    request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
  end
end

# things_controller_spec.rb
describe ThingssController do
   include XmlHttpHelper

       it "should provide a not acceptable response if user is not
appropriately related and it's an xhr" do
         login_as(@unrelated_child.login, 'secret')
         set_xhr_headers
         post :slug_collection, :name => 'bob', :collection_name =>
'my-things'
         response.status.should eql('406 Not Acceptable')
       end
end

Just a thought...
This topic is locked and can not be replied to.