From d3a11f0f8536c16aeb50c3ef3b61562d75dffc1a Mon Sep 17 00:00:00 2001 From: Jonas Nicklas Date: Thu, 26 Nov 2009 23:47:58 +0100 Subject: [PATCH] Streamline default_selector --- lib/capybara/dsl.rb | 10 +- lib/capybara/session.rb | 345 ++++++++++++++++++++-------------------- spec/dsl_spec.rb | 15 -- spec/session_spec.rb | 17 +- 4 files changed, 186 insertions(+), 201 deletions(-) diff --git a/lib/capybara/dsl.rb b/lib/capybara/dsl.rb index 62aa52ace..ed0a23482 100644 --- a/lib/capybara/dsl.rb +++ b/lib/capybara/dsl.rb @@ -1,7 +1,6 @@ module Capybara class << self attr_writer :default_driver, :current_driver, :javascript_driver - attr_reader :default_selector attr_accessor :app @@ -24,9 +23,7 @@ def use_default_driver def current_session session_pool["#{current_driver}#{app.object_id}"] ||= begin - session = Capybara::Session.new(current_driver, app) - session.default_selector = default_selector if default_selector - session + Capybara::Session.new(current_driver, app) end end @@ -37,11 +34,6 @@ def current_session? def reset_sessions! @session_pool = nil end - - def default_selector=(selector) - @default_selector = selector - current_session.default_selector = selector if current_session? - end private diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index 0bc309cfb..a31de1e4a 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -1,195 +1,200 @@ -class Capybara::Session - - FIELDS_PATHS = { - :text_field => proc { |id| "//input[@type='text'][@id='#{id}']" }, - :text_area => proc { |id| "//textarea[@id='#{id}']" }, - :password_field => proc { |id| "//input[@type='password'][@id='#{id}']" }, - :radio => proc { |id| "//input[@type='radio'][@id='#{id}']" }, - :hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}']" }, - :checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}']" }, - :select => proc { |id| "//select[@id='#{id}']" }, - :file_field => proc { |id| "//input[@type='file'][@id='#{id}']" } - } - - attr_reader :mode, :app - attr_writer :default_selector - - def initialize(mode, app) - @mode = mode - @app = app - end - - def driver - @driver ||= case mode - when :rack_test - Capybara::Driver::RackTest.new(app) - when :culerity - Capybara::Driver::Culerity.new(app) - when :selenium - Capybara::Driver::Selenium.new(app) - else - raise Capybara::DriverNotFoundError, "no driver called #{mode} was found" +module Capybara + + class << self + attr_writer :default_selector + + def default_selector + @default_selector ||= :xpath end end - - def default_selector - @default_selector ||= :xpath - end - - def visit(path) - driver.visit(path) - end - - def click_link(locator) - find_link(locator).click - end - def click_button(locator) - find_button(locator).click - end + class Session - def fill_in(locator, options={}) - find_field(locator, :text_field, :text_area, :password_field).set(options[:with]) - end - - def choose(locator) - find_field(locator, :radio).set(true) - end - - def check(locator) - find_field(locator, :checkbox).set(true) - end - - def uncheck(locator) - find_field(locator, :checkbox).set(false) - end - - def select(value, options={}) - find_field(options[:from], :select).select(value) - end - - def attach_file(locator, path) - find_field(locator, :file_field).set(path) - end + FIELDS_PATHS = { + :text_field => proc { |id| "//input[@type='text'][@id='#{id}']" }, + :text_area => proc { |id| "//textarea[@id='#{id}']" }, + :password_field => proc { |id| "//input[@type='password'][@id='#{id}']" }, + :radio => proc { |id| "//input[@type='radio'][@id='#{id}']" }, + :hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}']" }, + :checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}']" }, + :select => proc { |id| "//select[@id='#{id}']" }, + :file_field => proc { |id| "//input[@type='file'][@id='#{id}']" } + } - def body - driver.body - end - - def has_content?(content) - has_xpath?("//*[contains(.,'#{content}')]") - end - - def has_xpath?(path, options={}) - results = find(path) - if options[:text] - results = filter_by_text(results, options[:text]) + attr_reader :mode, :app + + def initialize(mode, app) + @mode = mode + @app = app end - if options[:count] - results.size == options[:count] - else - results.size > 0 + + def driver + @driver ||= case mode + when :rack_test + Capybara::Driver::RackTest.new(app) + when :culerity + Capybara::Driver::Culerity.new(app) + when :selenium + Capybara::Driver::Selenium.new(app) + else + raise Capybara::DriverNotFoundError, "no driver called #{mode} was found" + end end - end - - def has_css?(path, options={}) - has_xpath?(css_to_xpath(path), options) - end - - def within(kind, scope=nil) - kind, scope = default_selector, kind unless scope - scope = css_to_xpath(scope) if kind == :css - raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" if find(scope).empty? - scopes.push(scope) - yield - scopes.pop - end - - def within_fieldset(locator) - within "//fieldset[@id='#{locator}' or contains(legend,'#{locator}')]" do - yield + + def visit(path) + driver.visit(path) end - end - - def within_table(locator) - within "//table[@id='#{locator}' or contains(caption,'#{locator}')]" do - yield + + def click_link(locator) + find_link(locator).click end - end - - def save_and_open_page - require 'capybara/save_and_open_page' - Capybara::SaveAndOpenPage.save_and_open_page(body) - end - def find_field(locator, *kinds) - kinds = FIELDS_PATHS.keys if kinds.empty? - field = find_field_by_id(locator, *kinds) || find_field_by_label(locator, *kinds) - raise Capybara::ElementNotFound, "no field of kind #{kinds.inspect} with id or label '#{locator}' found" unless field - field - end - alias_method :field_labeled, :find_field - - def find_link(locator) - link = find("//a[@id='#{locator}' or contains(.,'#{locator}') or @title='#{locator}']").first - raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link - link - end - - def find_button(locator) - button = find("//input[@type='submit' or @type='image'][@id='#{locator}' or @value='#{locator}']").first - raise Capybara::ElementNotFound, "no button with value or id '#{locator}' found" unless button - button - end + def click_button(locator) + find_button(locator).click + end -private + def fill_in(locator, options={}) + find_field(locator, :text_field, :text_area, :password_field).set(options[:with]) + end - def css_to_xpath(css) - Nokogiri::CSS.xpath_for(css).first - end + def choose(locator) + find_field(locator, :radio).set(true) + end + + def check(locator) + find_field(locator, :checkbox).set(true) + end - def filter_by_text(nodes, text) - nodes.select do |node| - case text - when String - node.text.include?(text) - when Regexp - node.text =~ text + def uncheck(locator) + find_field(locator, :checkbox).set(false) + end + + def select(value, options={}) + find_field(options[:from], :select).select(value) + end + + def attach_file(locator, path) + find_field(locator, :file_field).set(path) + end + + def body + driver.body + end + + def has_content?(content) + has_xpath?("//*[contains(.,'#{content}')]") + end + + def has_xpath?(path, options={}) + results = find(path) + if options[:text] + results = filter_by_text(results, options[:text]) + end + if options[:count] + results.size == options[:count] + else + results.size > 0 end end - end - def current_scope - scopes.join('') - end + def has_css?(path, options={}) + has_xpath?(css_to_xpath(path), options) + end - def scopes - @scopes ||= [] - end + def within(kind, scope=nil) + kind, scope = Capybara.default_selector, kind unless scope + scope = css_to_xpath(scope) if kind == :css + raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" if find(scope).empty? + scopes.push(scope) + yield + scopes.pop + end - def find_field_by_id(locator, *kinds) - kinds.each do |kind| - path = FIELDS_PATHS[kind] - element = find(path.call(locator)).first - return element if element + def within_fieldset(locator) + within "//fieldset[@id='#{locator}' or contains(legend,'#{locator}')]" do + yield + end end - return nil - end - def find_field_by_label(locator, *kinds) - kinds.each do |kind| - label = find("//label[contains(.,'#{locator}')]").first - if label - element = find_field_by_id(label[:for], kind) + def within_table(locator) + within "//table[@id='#{locator}' or contains(caption,'#{locator}')]" do + yield + end + end + + def save_and_open_page + require 'capybara/save_and_open_page' + Capybara::SaveAndOpenPage.save_and_open_page(body) + end + + def find_field(locator, *kinds) + kinds = FIELDS_PATHS.keys if kinds.empty? + field = find_field_by_id(locator, *kinds) || find_field_by_label(locator, *kinds) + raise Capybara::ElementNotFound, "no field of kind #{kinds.inspect} with id or label '#{locator}' found" unless field + field + end + alias_method :field_labeled, :find_field + + def find_link(locator) + link = find("//a[@id='#{locator}' or contains(.,'#{locator}') or @title='#{locator}']").first + raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link + link + end + + def find_button(locator) + button = find("//input[@type='submit' or @type='image'][@id='#{locator}' or @value='#{locator}']").first + raise Capybara::ElementNotFound, "no button with value or id '#{locator}' found" unless button + button + end + + private + + def css_to_xpath(css) + Nokogiri::CSS.xpath_for(css).first + end + + def filter_by_text(nodes, text) + nodes.select do |node| + case text + when String + node.text.include?(text) + when Regexp + node.text =~ text + end + end + end + + def current_scope + scopes.join('') + end + + def scopes + @scopes ||= [] + end + + def find_field_by_id(locator, *kinds) + kinds.each do |kind| + path = FIELDS_PATHS[kind] + element = find(path.call(locator)).first return element if element end + return nil + end + + def find_field_by_label(locator, *kinds) + kinds.each do |kind| + label = find("//label[contains(.,'#{locator}')]").first + if label + element = find_field_by_id(label[:for], kind) + return element if element + end + end + return nil + end + + def find(locator) + locator = current_scope.to_s + locator + driver.find(locator) end - return nil - end - - def find(locator) - locator = current_scope.to_s + locator - driver.find(locator) end - end diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index 04f3b1465..f23267bcd 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -11,7 +11,6 @@ after do Capybara.default_driver = nil Capybara.use_default_driver - Capybara.default_selector = nil end describe '#default_driver' do @@ -109,20 +108,6 @@ end end - describe '#default_selector' do - after do - Capybara.default_selector = nil - end - - it "should set the selector used in current and future sessions" do - Capybara.default_selector.should_not eql(:css) - Capybara.default_selector = :css - Capybara.current_session.default_selector.should eql(:css) - Capybara.reset_sessions! - Capybara.current_session.default_selector.should eql(:css) - end - end - describe 'the DSL' do before do @session = Capybara diff --git a/spec/session_spec.rb b/spec/session_spec.rb index 8f9f90c45..436075d10 100644 --- a/spec/session_spec.rb +++ b/spec/session_spec.rb @@ -545,18 +545,21 @@ def extract_results(session) context "with the default selector" do it "should use XPath" do - lambda { - @session.within("//li[contains(., 'With Simple HTML')]") { } - }.should_not raise_error + @session.within("//li[contains(., 'With Simple HTML')]") do + @session.click_link('Go') + end + @session.body.should include('

Bar

') end end context "with the default selector set to CSS" do it "should use CSS" do - @session.default_selector = :css - lambda { - @session.within("ul li[contains('With Simple HTML')]") { } - }.should_not raise_error + Capybara.default_selector = :css + @session.within("ul li[contains('With Simple HTML')]") do + @session.click_link('Go') + end + @session.body.should include('

Bar

') + Capybara.default_selector = :xpath end end