From bcff9d7edabbd1658939fccbf89658dab7ba9e4c Mon Sep 17 00:00:00 2001 From: Dennis Rogenius Date: Fri, 11 Dec 2009 22:40:23 +0100 Subject: [PATCH 1/4] fixed a desciption and removed whitespaces --- lib/capybara/node.rb | 12 ++++++------ spec/driver/selenium_driver_spec.rb | 2 +- spec/session/selenium_session_spec.rb | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/capybara/node.rb b/lib/capybara/node.rb index 01b62237..6d3183db 100644 --- a/lib/capybara/node.rb +++ b/lib/capybara/node.rb @@ -9,11 +9,11 @@ class Capybara::Node def text raise "Not implemented" end - + def [](name) raise "Not implemented" end - + def value self[:value] end @@ -21,7 +21,7 @@ class Capybara::Node def set(value) raise "Not implemented" end - + def select(option) raise "Not implemented" end @@ -29,12 +29,12 @@ class Capybara::Node def click raise "Not implemented" end - + def drag_to(element) raise "Not implemented" end - + def tag_name raise "Not implemented" end -end \ No newline at end of file +end diff --git a/spec/driver/selenium_driver_spec.rb b/spec/driver/selenium_driver_spec.rb index 13021cba..fd0307ad 100644 --- a/spec/driver/selenium_driver_spec.rb +++ b/spec/driver/selenium_driver_spec.rb @@ -4,7 +4,7 @@ describe Capybara::Driver::Selenium do before do @driver = Capybara::Driver::Selenium.new(TestApp) end - + it_should_behave_like "driver" it_should_behave_like "driver with javascript support" end diff --git a/spec/session/selenium_session_spec.rb b/spec/session/selenium_session_spec.rb index b31afca7..ad1f61fb 100644 --- a/spec/session/selenium_session_spec.rb +++ b/spec/session/selenium_session_spec.rb @@ -5,19 +5,19 @@ describe Capybara::Session do before do @session = Capybara::Session.new(:selenium, TestApp) end - + describe '#driver' do - it "should be a rack test driver" do + it "should be a selenium driver" do @session.driver.should be_an_instance_of(Capybara::Driver::Selenium) end end - + describe '#mode' do it "should remember the mode" do @session.mode.should == :selenium end end - + it_should_behave_like "session" end end From 786708406335d12c5e856d75a433c77142c4dba7 Mon Sep 17 00:00:00 2001 From: Dennis Rogenius Date: Fri, 11 Dec 2009 22:41:12 +0100 Subject: [PATCH 2/4] added evaluate_script method to all javascipt drivers for evaluating javascript --- lib/capybara/driver/culerity_driver.rb | 24 ++++++++++++++---------- lib/capybara/driver/selenium_driver.rb | 4 ++++ spec/drivers_spec.rb | 17 ++++++++++++----- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/capybara/driver/culerity_driver.rb b/lib/capybara/driver/culerity_driver.rb index b0f3d4d4..c7756ac6 100644 --- a/lib/capybara/driver/culerity_driver.rb +++ b/lib/capybara/driver/culerity_driver.rb @@ -5,7 +5,7 @@ class Capybara::Driver::Culerity def text node.text end - + def [](name) value = if name.to_sym == :class node.class_name @@ -18,28 +18,28 @@ class Capybara::Driver::Culerity def set(value) node.set(value) end - + def select(option) node.select(option) end - + def click node.click end - + def drag_to(element) node.fire_event('mousedown') element.node.fire_event('mousemove') element.node.fire_event('mouseup') end - + def tag_name # FIXME: this might be the dumbest way ever of getting the tag name # there has to be something better... node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1] end end - + attr_reader :app, :rack_server def self.server @@ -57,25 +57,29 @@ class Capybara::Driver::Culerity @rack_server = Capybara::Server.new(@app) @rack_server.boot end - + def visit(path) browser.goto(url(path)) end - + def body browser.html end - + def find(selector) browser.elements_by_xpath(selector).map { |node| Node.new(self, node) } end + def evaluate_script(script) + browser.execute_script "#{script}" + end + private def url(path) rack_server.url(path) end - + def browser unless @_browser @_browser = ::Culerity::RemoteBrowserProxy.new self.class.server, {:browser => :firefox, :log_level => :off} diff --git a/lib/capybara/driver/selenium_driver.rb b/lib/capybara/driver/selenium_driver.rb index b5b8db5b..2b48ce7b 100644 --- a/lib/capybara/driver/selenium_driver.rb +++ b/lib/capybara/driver/selenium_driver.rb @@ -84,6 +84,10 @@ class Capybara::Driver::Selenium driver.find_elements(:xpath, selector).map { |node| Node.new(self, node) } end + def evaluate_script(script) + driver.execute_script "return #{script}" + end + private def url(path) diff --git a/spec/drivers_spec.rb b/spec/drivers_spec.rb index 064f5196..df0cd055 100644 --- a/spec/drivers_spec.rb +++ b/spec/drivers_spec.rb @@ -37,7 +37,7 @@ shared_examples_for 'driver' do @driver.find('//a')[0].text.should == 'labore' @driver.find('//a')[1].text.should == 'ullamco' end - + it "should extract node attributes" do @driver.find('//a')[0][:href].should == '/with_simple_html' @driver.find('//a')[0][:class].should == 'simple' @@ -51,7 +51,7 @@ shared_examples_for 'driver' do @driver.find('//input').first.set('gorilla') @driver.find('//input').first.value.should == 'gorilla' end - + it "should extract node tag name" do @driver.find('//a')[0].tag_name.should == 'a' @driver.find('//a')[1].tag_name.should == 'a' @@ -63,20 +63,27 @@ shared_examples_for 'driver' do end shared_examples_for "driver with javascript support" do + before { @driver.visit('/with_js') } + describe '#find' do it "should find dynamically changed nodes" do - @driver.visit('/with_js') @driver.find('//p').first.text.should == 'I changed it' end end - + describe '#drag_to' do it "should drag and drop an object" do - @driver.visit('/with_js') draggable = @driver.find('//div[@id="drag"]').first droppable = @driver.find('//div[@id="drop"]').first draggable.drag_to(droppable) @driver.find('//div[contains(., "Dropped!")]').should_not be_nil end end + + describe "#evaluate_script" do + it "should return the value of the executed script" do + @driver.evaluate_script('1+1').should == 2 + end + end + end From 525ba2d654fea65d77b05c4b3c320b8b04007c49 Mon Sep 17 00:00:00 2001 From: Dennis Rogenius Date: Sat, 12 Dec 2009 13:33:00 +0100 Subject: [PATCH 3/4] removed whitespace and added dsl shortcut to evaluate_script method --- lib/capybara.rb | 5 +- lib/capybara/dsl.rb | 8 +- lib/capybara/session.rb | 10 ++- spec/dsl_spec.rb | 7 +- spec/session/culerity_session_spec.rb | 7 +- spec/session/rack_test_session_spec.rb | 7 +- spec/session/selenium_session_spec.rb | 1 + spec/session_spec.rb | 103 +++++++++++++++---------- 8 files changed, 92 insertions(+), 56 deletions(-) diff --git a/lib/capybara.rb b/lib/capybara.rb index 986ca7cf..3da631ec 100644 --- a/lib/capybara.rb +++ b/lib/capybara.rb @@ -6,6 +6,7 @@ module Capybara class CapybaraError < StandardError; end class DriverNotFoundError < CapybaraError; end class ElementNotFound < CapybaraError; end + class NotSupportedByDriverError < CapybaraError; end class << self attr_accessor :debug, :asset_root @@ -14,13 +15,13 @@ module Capybara def default_selector @default_selector ||= :xpath end - + def log(message) puts "[capybara] #{message}" if debug true end end - + autoload :Server, 'capybara/server' autoload :Session, 'capybara/session' autoload :Node, 'capybara/node' diff --git a/lib/capybara/dsl.rb b/lib/capybara/dsl.rb index b86a7b79..09de7c54 100644 --- a/lib/capybara/dsl.rb +++ b/lib/capybara/dsl.rb @@ -12,7 +12,7 @@ module Capybara @current_driver || default_driver end alias_method :mode, :current_driver - + def javascript_driver @javascript_driver || :selenium end @@ -24,11 +24,11 @@ module Capybara def current_session session_pool["#{current_driver}#{app.object_id}"] ||= Capybara::Session.new(current_driver, app) end - + def current_session? session_pool.has_key?("#{current_driver}#{app.object_id}") end - + def reset_sessions! @session_pool = nil end @@ -50,7 +50,7 @@ module Capybara :visit, :body, :click_link, :click_button, :fill_in, :choose, :has_xpath?, :has_css?, :check, :uncheck, :attach_file, :select, :has_content?, :within, :within_fieldset, :within_table, :save_and_open_page, :find, :find_field, :find_link, :find_button, - :field_labeled, :all + :field_labeled, :all, :evaluate_script ] SESSION_METHODS.each do |method| class_eval <<-RUBY, __FILE__, __LINE__+1 diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index 967c4d53..4f9cb8fd 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -138,7 +138,7 @@ module Capybara locator = current_scope.to_s + locator driver.find(locator) end - + def find(locator) all(locator).first end @@ -158,6 +158,14 @@ module Capybara button || find("//button[@id='#{locator}' or @value='#{locator}' or contains(.,'#{locator}')]") end + def evaluate_script(script) + begin + driver.evaluate_script(script) + rescue NoMethodError + raise NotSupportedByDriverError + end + end + private def css_to_xpath(css) diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index f23267bc..1ed60a4f 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -36,7 +36,7 @@ describe Capybara do Capybara.current_driver.should == :culerity end end - + describe '#javascript_driver' do it "should default to selenium" do Capybara.javascript_driver.should == :selenium @@ -98,7 +98,7 @@ describe Capybara do Capybara.current_session.app.should == Capybara.app end end - + describe '.reset_sessions!' do it "should clear any persisted sessions" do object_id = Capybara.current_session.object_id @@ -114,6 +114,7 @@ describe Capybara do end it_should_behave_like "session" + it_should_behave_like "session without javascript driver" it "should be possible to include it in another class" do klass = Class.new do @@ -124,7 +125,7 @@ describe Capybara do foo.click_link('ullamco') foo.body.should include('Another World') end - + it "should provide a 'page' shortcut for more expressive tests" do klass = Class.new do include Capybara diff --git a/spec/session/culerity_session_spec.rb b/spec/session/culerity_session_spec.rb index 2a377966..e314f1a3 100644 --- a/spec/session/culerity_session_spec.rb +++ b/spec/session/culerity_session_spec.rb @@ -5,19 +5,20 @@ describe Capybara::Session do before do @session = Capybara::Session.new(:culerity, TestApp) end - + describe '#driver' do it "should be a rack test driver" do @session.driver.should be_an_instance_of(Capybara::Driver::Culerity) end end - + describe '#mode' do it "should remember the mode" do @session.mode.should == :culerity end end - + it_should_behave_like "session" + it_should_behave_like "session with javascript driver" end end diff --git a/spec/session/rack_test_session_spec.rb b/spec/session/rack_test_session_spec.rb index f45619a3..5d4a15d6 100644 --- a/spec/session/rack_test_session_spec.rb +++ b/spec/session/rack_test_session_spec.rb @@ -5,19 +5,20 @@ describe Capybara::Session do before do @session = Capybara::Session.new(:rack_test, TestApp) end - + describe '#driver' do it "should be a rack test driver" do @session.driver.should be_an_instance_of(Capybara::Driver::RackTest) end end - + describe '#mode' do it "should remember the mode" do @session.mode.should == :rack_test end end - + it_should_behave_like "session" + it_should_behave_like "session without javascript driver" end end diff --git a/spec/session/selenium_session_spec.rb b/spec/session/selenium_session_spec.rb index ad1f61fb..4656157d 100644 --- a/spec/session/selenium_session_spec.rb +++ b/spec/session/selenium_session_spec.rb @@ -19,5 +19,6 @@ describe Capybara::Session do end it_should_behave_like "session" + it_should_behave_like "session with javascript driver" end end diff --git a/spec/session_spec.rb b/spec/session_spec.rb index 2b3d5910..4ff14c7f 100644 --- a/spec/session_spec.rb +++ b/spec/session_spec.rb @@ -185,7 +185,7 @@ shared_examples_for "session" do end.should raise_error(Capybara::ElementNotFound) end end - + it "should serialize and send GET forms" do @session.visit('/form') @session.click_button('med') @@ -272,7 +272,7 @@ shared_examples_for "session" do @session.click_button('awesome') extract_results(@session)['gender'].should == 'both' end - + context "with a locator that doesn't exist" do it "should raise an error" do running { @session.choose('does not exist') }.should raise_error(Capybara::ElementNotFound) @@ -296,7 +296,7 @@ shared_examples_for "session" do @session.click_button('awesome') extract_results(@session)['pets'].should include('dog', 'cat', 'hamster') end - + context "with a locator that doesn't exist" do it "should raise an error" do running { @session.check('does not exist') }.should raise_error(Capybara::ElementNotFound) @@ -322,7 +322,7 @@ shared_examples_for "session" do extract_results(@session)['pets'].should include('dog') extract_results(@session)['pets'].should_not include('hamster') end - + context "with a locator that doesn't exist" do it "should raise an error" do running { @session.uncheck('does not exist') }.should raise_error(Capybara::ElementNotFound) @@ -346,7 +346,7 @@ shared_examples_for "session" do @session.click_button('awesome') extract_results(@session)['locale'].should == 'fi' end - + context "with a locator that doesn't exist" do it "should raise an error" do running { @session.select('foo', :from => 'does not exist') }.should raise_error(Capybara::ElementNotFound) @@ -400,7 +400,7 @@ shared_examples_for "session" do @session.should_not have_xpath("//p//a[@id='doesnotexist']") @session.should_not have_xpath("//p[contains(.,'thisstringisnotonpage')]") end - + it "should respect scopes" do @session.within "//p[@id='first']" do @session.should have_xpath("//a[@id='foo']") @@ -426,20 +426,20 @@ shared_examples_for "session" do @session.should_not have_xpath("//p//a[@id='doesnotexist']", :count => 1) end end - + context "with text" do it "should discard all matches where the given string is not contained" do @session.should have_xpath("//p//a", :text => "Redirect", :count => 1) @session.should_not have_xpath("//p", :text => "Doesnotexist") end - + it "should discard all matches where the given regexp is not matched" do @session.should have_xpath("//p//a", :text => /re[dab]i/i, :count => 1) @session.should_not have_xpath("//p//a", :text => /Red$/) end end end - + describe '#has_css?' do before do @session.visit('/with_html') @@ -455,7 +455,7 @@ shared_examples_for "session" do @session.should_not have_css("p a#doesnotexist") @session.should_not have_css("p.nosuchclass") end - + it "should respect scopes" do @session.within "//p[@id='first']" do @session.should have_css("a#foo") @@ -479,13 +479,13 @@ shared_examples_for "session" do @session.should_not have_css("p a.doesnotexist", :count => 1) end end - + context "with text" do it "should discard all matches where the given string is not contained" do @session.should have_css("p a", :text => "Redirect", :count => 1) @session.should_not have_css("p a", :text => "Doesnotexist") end - + it "should discard all matches where the given regexp is not matched" do @session.should have_css("p a", :text => /re[dab]i/i, :count => 1) @session.should_not have_css("p a", :text => /Red$/) @@ -533,14 +533,14 @@ shared_examples_for "session" do @session.click_button('Upload') end end - + context "with a locator that doesn't exist" do it "should raise an error" do running { @session.attach_file('does not exist', 'foo.txt') }.should raise_error(Capybara::ElementNotFound) end end end - + describe '#find_field' do before do @session.visit('/form') @@ -551,22 +551,22 @@ shared_examples_for "session" do @session.find_field('form_description').text.should == 'Descriptive text goes here' @session.find_field('Region')[:name].should == 'form[region]' end - + it "should raise an error if the field doesn't exist" do @session.find_field('Does not exist').should be_nil end - + it "should find only given kind of field" do @session.find_field('form_description', :text_field, :text_area).text.should == 'Descriptive text goes here' @session.find_field('form_description', :password_field).should be_nil end - + it "should be aliased as 'field_labeled' for webrat compatibility" do @session.field_labeled('Dog').value.should == 'dog' @session.field_labeled('Does not exist').should be_nil end end - + describe '#find_link' do before do @session.visit('/with_html') @@ -576,12 +576,12 @@ shared_examples_for "session" do @session.find_link('foo').text.should == "ullamco" @session.find_link('labore')[:href].should == "/with_simple_html" end - + it "should return nil if the field doesn't exist" do @session.find_link('Does not exist').should be_nil end end - + describe '#find_button' do before do @session.visit('/form') @@ -591,7 +591,7 @@ shared_examples_for "session" do @session.find_button('med')[:id].should == "mediocre" @session.find_button('crap321').value.should == "crappy" end - + it "should return nil if the field doesn't exist" do @session.find_button('Does not exist').should be_nil end @@ -607,11 +607,11 @@ shared_examples_for "session" do @session.all('//h1').first.text.should == 'This is a test' @session.all("//input[@id='test_field']").first[:value].should == 'monkey' end - + it "should return an empty array when nothing was found" do @session.all('//div').should be_empty end - + context "within a scope" do before do @session.visit('/with_scope') @@ -620,7 +620,7 @@ shared_examples_for "session" do it "should find any element using the given locator" do @session.within(:xpath, "//div[@id='for_bar']") do @session.all('//li').should have(2).elements - end + end end end end @@ -634,11 +634,11 @@ shared_examples_for "session" do @session.find('//h1').text.should == 'This is a test' @session.find("//input[@id='test_field']")[:value].should == 'monkey' end - + it "should return nil when nothing was found" do @session.find('//div').should be_nil end - + context "within a scope" do before do @session.visit('/with_scope') @@ -647,7 +647,7 @@ shared_examples_for "session" do it "should find the first element using the given locator" do @session.within(:xpath, "//div[@id='for_bar']") do @session.find('//li').text.should =~ /With Simple HTML/ - end + end end end end @@ -656,7 +656,7 @@ shared_examples_for "session" do before do @session.visit('/with_scope') end - + context "with CSS selector" do it "should click links in the given scope" do @session.within(:css, "ul li[contains('With Simple HTML')]") do @@ -665,7 +665,7 @@ shared_examples_for "session" do @session.body.should include('

Bar

') end end - + context "with XPath selector" do it "should click links in the given scope" do @session.within(:xpath, "//li[contains(.,'With Simple HTML')]") do @@ -674,7 +674,7 @@ shared_examples_for "session" do @session.body.should include('

Bar

') end end - + context "with the default selector" do it "should use XPath" do @session.within("//li[contains(., 'With Simple HTML')]") do @@ -683,12 +683,12 @@ shared_examples_for "session" do @session.body.should include('

Bar

') end end - + context "with the default selector set to CSS" do after do Capybara.default_selector = :xpath end - + it "should use CSS" do Capybara.default_selector = :css @session.within("ul li[contains('With Simple HTML')]") do @@ -697,7 +697,7 @@ shared_examples_for "session" do @session.body.should include('

Bar

') end end - + context "with click_link" do it "should click links in the given scope" do @session.within("//li[contains(.,'With Simple HTML')]") do @@ -725,7 +725,7 @@ shared_examples_for "session" do @session.body.should include('Hello world') end end - + it "should raise an error if the scope is not found on the page" do running { @session.within("//div[@id='doesnotexist']") do @@ -749,12 +749,12 @@ shared_examples_for "session" do end end end - + describe '#within_fieldset' do before do @session.visit('/fieldsets') end - + it "should restrict scope to a fieldset given by id" do @session.within_fieldset("villain_fieldset") do @session.fill_in("Name", :with => 'Goldfinger') @@ -762,7 +762,7 @@ shared_examples_for "session" do end extract_results(@session)['villain_name'].should == 'Goldfinger' end - + it "should restrict scope to a fieldset given by legend" do @session.within_fieldset("Villain") do @session.fill_in("Name", :with => 'Goldfinger') @@ -771,12 +771,12 @@ shared_examples_for "session" do extract_results(@session)['villain_name'].should == 'Goldfinger' end end - + describe '#within_table' do before do @session.visit('/tables') end - + it "should restrict scope to a fieldset given by id" do @session.within_table("girl_table") do @session.fill_in("Name", :with => 'Christmas') @@ -784,7 +784,7 @@ shared_examples_for "session" do end extract_results(@session)['girl_name'].should == 'Christmas' end - + it "should restrict scope to a fieldset given by legend" do @session.within_table("Villain") do @session.fill_in("Name", :with => 'Quantum') @@ -793,6 +793,28 @@ shared_examples_for "session" do extract_results(@session)['villain_name'].should == 'Quantum' end end + +end + +shared_examples_for "session with javascript driver" do + describe "#evaluate_script" do + before{ @session.visit('/with_js') } + it "should return the evaluated script" do + @session.evaluate_script("1+3").should == 4 + end + end +end + + +shared_examples_for "session without javascript driver" do + describe "#evaluate_script" do + before{ @session.visit('/with_js') } + it "should raise an error" do + running { + @session.evaluate_script("1+5") + }.should raise_error(Capybara::NotSupportedByDriverError) + end + end end describe Capybara::Session do @@ -804,3 +826,4 @@ describe Capybara::Session do end end end + From fb71b29f9fc68c7a9529dfc3bc436d3d0f7ed807 Mon Sep 17 00:00:00 2001 From: Dennis Rogenius Date: Sat, 12 Dec 2009 13:37:29 +0100 Subject: [PATCH 4/4] added documentation on the evaluate_script method --- README.rdoc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index 5f6b06e4..2f5277b3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -119,6 +119,10 @@ Querying: find_button field_labeled +Scripting: + + evaluate_script – Returns the value of the executed javascript (only works on javascript supported drivers) + Debugging: save_and_open_page @@ -158,7 +162,7 @@ For ultimate control, you can instantiate and use a session manually. Capybara does not try to guess what kind of selector you are going to give it, if you want to use CSS with your 'within' declarations for example, you'll need to do: - + within(:css, 'ul li') { ... } Alternatively you can set the default selector to CSS, which may help if you are @@ -233,4 +237,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.