Major internals changes, some (few) API breaking! Some nifty new stuff!

165 views
Skip to first unread message

jnicklas

unread,
Jul 10, 2010, 11:30:59 AM7/10/10
to Capybara
I just heavily refactored the internals of Capybara, hopefully it
should be better structured and less messy now than it's been before.
These changes enable some major cool new functionality and some
breaking changes:

== All nodes returned by Capybara expose all session methods:

page.locate(:css, '#foo').fill_in('Bar', :with => 'baz')

page.locate(:css, '#foo').tap do |foo|
foo.fill_in('Bar', :with => 'baz')
foo.click_button('Go')
end

This is really the biggest change, hopefully it should make fore much
cleaner and more readable specs.

== locate now takes the same arguments as other finder methods

page.locate('li', :text => 'Something').click_link('Delete')

Compare this to the XPath mess that would have been necessary before
and it's easy to see why this is a huge win.

== Node class has been split and broken up

There is no a driver internal node, Capybara::Driver::Node, and a
Capybara::Node. The latter contains most of the methods that were in
Capybara::Session before, broken up over three mixins, Finders,
Matchers and Actions. With this change me minimize the interface
between Capybara and its Drivers.

API BREAKING CHANGES:

== Within now follows the XPath spec when it comes to scoping.

In XPath, '//' means "anywhere in the document" not "anywhere relative
to the current node", the latter is actually represented by: './/'. So
the following:

within('//body') do
page.find('//script')
end

finds all script tags inside the entire document, not only those
inside the body, like you might expect. This is what you want:

within('//body') do
page.find('.//script')
end

Capybara used to get this wrong, because of the way that scopes and
within worked. Now it is according to the spec, which might not be how
you expect it to work.

== Session#click has been renamed to Session#click_link_or_button

Otherwise it would have conflicted with Node#click now that all Nodes
expose all DSL methods. This method has generated some confusion
before, with people trying to pass in CSS/XPath selectors into it, I
think the rename is for the better.

== Node#select renamed Node#select_option, Node#unselect becomes
Node#unselect_option

Again, to avoid naming conflicts.

Elliot Winkler

unread,
Jul 10, 2010, 7:03:34 PM7/10/10
to ruby-c...@googlegroups.com
On Sat, Jul 10, 2010 at 10:30 AM, jnicklas <jonas....@gmail.com> wrote:
I just heavily refactored the internals of Capybara, hopefully it
should be better structured and less messy now than it's been before.
These changes enable some major cool new functionality and some
breaking changes:

== All nodes returned by Capybara expose all session methods:

page.locate(:css, '#foo').fill_in('Bar', :with => 'baz')

page.locate(:css, '#foo').tap do |foo|
 foo.fill_in('Bar', :with => 'baz')
 foo.click_button('Go')
end

This is really the biggest change, hopefully it should make fore much
cleaner and more readable specs.

Awesome! Makes total sense -- I was just noticing the other day how nice it would be to do this.
 
== Session#click has been renamed to Session#click_link_or_button

Otherwise it would have conflicted with Node#click now that all Nodes
expose all DSL methods. This method has generated some confusion
before, with people trying to pass in CSS/XPath selectors into it, I
think the rename is for the better.

Thanks for letting us know. I like the brevity of "click", but I guess I can always alias it in my app.

-- Elliot

Jonas Nicklas

unread,
Jul 11, 2010, 8:24:22 AM7/11/10
to Capybara
I actually forgot one subtle change, which might break existing specs.
Within works differently now, with respect to *when* the scope is
resolved. Imagine the following HTML:

<div class="contact">
<h1 id="foo1">Foo 1</h1>
</div>
<div class="contact">
<h1 id="foo2">Foo 2</h1>
</div>

and then the following code:

within('.contact') do
page.should have_css('#foo2')
end

Previously this would have passed, now it will fail! That's because
within will now grab the first element where it's locator matches, and
then execute the code block within that element.

/Jonas

Rick DeNatale

unread,
Jul 12, 2010, 1:00:49 PM7/12/10
to ruby-c...@googlegroups.com
On Sun, Jul 11, 2010 at 8:24 AM, Jonas Nicklas <jonas....@gmail.com> wrote:
> I actually forgot one subtle change, which might break existing specs.
> Within works differently now, with respect to *when* the scope is
> resolved. Imagine the following HTML:
>
>    <div class="contact">
>      <h1 id="foo1">Foo 1</h1>
>    </div>
>    <div class="contact">
>      <h1 id="foo2">Foo 2</h1>
>    </div>
>
> and then the following code:
>
>    within('.contact') do
>      page.should have_css('#foo2')
>    end
>
> Previously this would have passed, now it will fail! That's because
> within will now grab the first element where it's locator matches, and
> then execute the code block within that element.

Might this be the result of another breaking change?

And the "Description" field should contain "Quos (OTC) x 3 [3
times]" # features/step_definitions/web_steps.rb:149
expected: /Quos (OTC) x 3 [3 times]/,
got: "Quos (OTC) x 3 [3 times]" (using =~)

I tried upgrading my cucumber/capybara/webdriver stack to see if I
could alleviate the probolems I'm seeing with ajax scenarios. I see
this with both capybara 0.3.9 and 0.3.8. (I had been using 0.3.5)

And I can't quite figure out where script/cucumber install --capybara
is getting feature/steps/web_steps.rb from.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Jonas Nicklas

unread,
Jul 12, 2010, 2:02:51 PM7/12/10
to ruby-c...@googlegroups.com
@Matt:

the problem is that locate is used internally for all kinds of stuff,
like finding fields for fill_in and friends. It might not make sense
to raise an error for those.

@Rick:

That's a really weird error, but afaik I haven't touched that part of
the code. Also the generated steps come from cucumber/rails not
Capybara.

/Jonas

Reply all
Reply to author
Forward
0 new messages