diff --git a/lib/webcat/cucumber.rb b/lib/webcat/cucumber.rb new file mode 100644 index 00000000..eea29628 --- /dev/null +++ b/lib/webcat/cucumber.rb @@ -0,0 +1,9 @@ +require 'webcat' +require 'webcat/dsl' + +if defined?(Rails) + Webcat.app = ActionController::Dispatcher.new +end + +World(Webcat) + diff --git a/lib/webcat/driver/selenium/rc_server.rb b/lib/webcat/driver/selenium/rc_server.rb deleted file mode 100644 index 9ce72258..00000000 --- a/lib/webcat/driver/selenium/rc_server.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Webcat - module Selenium - - class SeleniumRCServer - unless Kernel.respond_to?(:silence_stream) - def silence_stream(stream) - old_stream = stream.dup - stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null') - stream.sync = true - yield - ensure - stream.reopen(old_stream) - end - end - - def booted? - @booted || false - end - - def boot - return if booted? - silence_stream(STDOUT) do - remote_control.start :background => true - end - wait - @booted = true - at_exit do - silence_stream(STDOUT) do - remote_control.stop - end - end - end - - def remote_control - return @remote_control if @remote_control - - @remote_control = ::Selenium::RemoteControl::RemoteControl.new("0.0.0.0", 5041, 15) - @remote_control.jar_file = jar_path - - return @remote_control - end - - def jar_path - File.expand_path("selenium-server.jar", File.dirname(__FILE__)) - end - - def wait - return true - $stderr.print "==> Waiting for Selenium RC server on port #{Webrat.configuration.selenium_server_port}... " - TCPSocket.wait_for_service_with_timeout(:host => "localhost", :port => 5041, :timeout => 15) - $stderr.print "Ready!\n" - rescue SocketError - $stderr.puts "==> Failed to boot the Selenium RC server... exiting!" - exit - end - - end - - end -end - diff --git a/lib/webcat/driver/selenium/selenium-server.jar b/lib/webcat/driver/selenium/selenium-server.jar deleted file mode 100644 index 7e029034..00000000 Binary files a/lib/webcat/driver/selenium/selenium-server.jar and /dev/null differ diff --git a/lib/webcat/driver/selenium_driver.rb b/lib/webcat/driver/selenium_driver.rb index 190c742c..34a1ef2a 100644 --- a/lib/webcat/driver/selenium_driver.rb +++ b/lib/webcat/driver/selenium_driver.rb @@ -1,57 +1,83 @@ -require 'selenium/client' -require 'webcat/driver/selenium/rc_server' -require 'webcat/core_ext/tcp_socket' - +require 'selenium-webdriver' class Webcat::Driver::Selenium class Node < Struct.new(:node) def text node.text end - - def attribute(name) - value = if name.to_sym == :class - node.class_name + + def [](name) + if name == :value + node.value else - node.send(name.to_sym) + node.attribute(name) end - return value if value and not value.empty? + rescue Selenium::WebDriver::Error::WebDriverError + nil end - + + def set(value) + if tag_name == 'input' and %w(text password hidden file).include?(type) + node.clear + node.send_keys(value.to_s) + elsif tag_name == 'input' and type == 'radio' + node.select + elsif tag_name == 'input' and type == 'checkbox' + node.toggle + elsif tag_name == "textarea" + node.clear + node.send_keys(value.to_s) + end + end + + def select(option) + node.find_element(:xpath, ".//option[text()='#{option}']").select + end + def click node.click 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] + node.tag_name end + + private + + def type + self[:type] + end + end - + attr_reader :app, :rack_server - def self.server - @server ||= Webcat::Selenium::SeleniumRCServer.new + def self.driver + unless @driver + @driver = Selenium::WebDriver.for :firefox + at_exit do + @driver.quit + end + end + @driver end def initialize(app) @app = app @rack_server = Webcat::Server.new(@app) @rack_server.boot - self.class.server.boot end - + def visit(path) - browser.open(url(path)) + driver.navigate.to(url(path)) end - + def body - browser.html + driver.page_source end - + def find(selector) - browser.elements_by_xpath(selector).map { |node| Node.new(node) } + driver.find_elements(:xpath, selector).map { |node| Node.new(node) } end private @@ -59,22 +85,9 @@ private def url(path) rack_server.url(path) end - - def browser - unless @_browser - @_browser = Selenium::Client::Driver.new :host => 'localhost', - :port => 4444, - :browser => "*firefox", - :url => rack_server.url('/'), - :timeout_in_second => 10 - @_browser.start_new_browser_session - - at_exit do - @_browser.close_current_browser_session - end - end - @_browser + + def driver + self.class.driver end end - diff --git a/lib/webcat/session.rb b/lib/webcat/session.rb index a0f0594c..6c547854 100644 --- a/lib/webcat/session.rb +++ b/lib/webcat/session.rb @@ -12,6 +12,8 @@ class Webcat::Session Webcat::Driver::RackTest.new(app) when :culerity Webcat::Driver::Culerity.new(app) + when :selenium + Webcat::Driver::Selenium.new(app) else raise Webcat::DriverNotFoundError, "no driver called #{mode} was found" end diff --git a/spec/driver/selenium_driver_spec.rb b/spec/driver/selenium_driver_spec.rb index 81a7eb31..b9b22861 100644 --- a/spec/driver/selenium_driver_spec.rb +++ b/spec/driver/selenium_driver_spec.rb @@ -5,6 +5,6 @@ describe Webcat::Driver::Selenium do @driver = Webcat::Driver::Selenium.new(TestApp) end - # it_should_behave_like "driver" - # it_should_behave_like "driver with javascript support" + it_should_behave_like "driver" + it_should_behave_like "driver with javascript support" end diff --git a/spec/drivers_spec.rb b/spec/drivers_spec.rb index 85ead06b..243bcddd 100644 --- a/spec/drivers_spec.rb +++ b/spec/drivers_spec.rb @@ -30,7 +30,7 @@ shared_examples_for 'driver' do end it "should find the correct number of elements" do - @driver.find('//a').size.should == 2 + @driver.find('//a').size.should == 3 end it "should extract node texts" do diff --git a/spec/fixtures/test_file.txt b/spec/fixtures/test_file.txt new file mode 100644 index 00000000..0cd972cb --- /dev/null +++ b/spec/fixtures/test_file.txt @@ -0,0 +1 @@ +ThisIsTheTestFile \ No newline at end of file diff --git a/spec/session/selenium_session_spec.rb b/spec/session/selenium_session_spec.rb new file mode 100644 index 00000000..238bc10d --- /dev/null +++ b/spec/session/selenium_session_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../spec_helper', File.dirname(__FILE__)) + +describe Webcat::Session do + context 'with selenium driver' do + before do + @session = Webcat::Session.new(:selenium, TestApp) + end + + describe '#driver' do + it "should be a rack test driver" do + @session.driver.should be_an_instance_of(Webcat::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 diff --git a/spec/session_spec.rb b/spec/session_spec.rb index 1ffdc7be..95abf6a5 100644 --- a/spec/session_spec.rb +++ b/spec/session_spec.rb @@ -1,6 +1,12 @@ require File.expand_path('spec_helper', File.dirname(__FILE__)) +require 'nokogiri' + shared_examples_for "session" do + def extract_results(session) + YAML.load Nokogiri::HTML(session.body).xpath("//pre[@id='results']").first.text + end + describe '#app' do it "should remember the application" do @session.app.should == TestApp @@ -10,9 +16,9 @@ shared_examples_for "session" do describe '#visit' do it "should fetch a response from the driver" do @session.visit('/') - @session.body.should == 'Hello world!' + @session.body.should include('Hello world!') @session.visit('/foo') - @session.body.should == 'Another World' + @session.body.should include('Another World') end end @@ -24,21 +30,21 @@ shared_examples_for "session" do context "with id given" do it "should take user to the linked page" do @session.click_link('foo') - @session.body.should == 'Another World' + @session.body.should include('Another World') end end context "with text given" do it "should take user to the linked page" do @session.click_link('labore') - @session.body.should == '

Bar

' + @session.body.should include('

Bar

') end end context "with title given" do it "should take user to the linked page" do @session.click_link('awesome title') - @session.body.should == '

Bar

' + @session.body.should include('

Bar

') end end @@ -64,7 +70,7 @@ shared_examples_for "session" do context "with value given on a submit button" do before do @session.click_button('awesome') - @results = YAML.load(@session.body) + @results = extract_results(@session) end it "should serialize and submit text fields" do @@ -121,24 +127,21 @@ shared_examples_for "session" do context "with id given on a submit button" do it "should submit the associated form" do @session.click_button('awe123') - results = YAML.load(@session.body) - results['first_name'].should == 'John' + extract_results(@session)['first_name'].should == 'John' end end context "with value given on an image button" do it "should submit the associated form" do @session.click_button('okay') - results = YAML.load(@session.body) - results['first_name'].should == 'John' + extract_results(@session)['first_name'].should == 'John' end end context "with id given on an image button" do it "should submit the associated form" do @session.click_button('okay556') - results = YAML.load(@session.body) - results['first_name'].should == 'John' + extract_results(@session)['first_name'].should == 'John' end end @@ -156,39 +159,39 @@ shared_examples_for "session" do it "should fill in a text field by id" do @session.fill_in('form_first_name', :with => 'Harry') @session.click_button('awesome') - YAML.load(@session.body)['first_name'].should == 'Harry' + extract_results(@session)['first_name'].should == 'Harry' end it "should fill in a text field by label" do @session.fill_in('First Name', :with => 'Harry') @session.click_button('awesome') - YAML.load(@session.body)['first_name'].should == 'Harry' + extract_results(@session)['first_name'].should == 'Harry' end it "should fill in a textarea by id" do @session.fill_in('form_description', :with => 'Texty text') @session.click_button('awesome') - YAML.load(@session.body)['description'].should == 'Texty text' + extract_results(@session)['description'].should == 'Texty text' end it "should fill in a textarea by label" do @session.fill_in('Description', :with => 'Texty text') @session.click_button('awesome') - YAML.load(@session.body)['description'].should == 'Texty text' + extract_results(@session)['description'].should == 'Texty text' end it "should fill in a password field by id" do pending "Culerity doesn't like password fields for some reason" if @session.mode == :culerity @session.fill_in('form_password', :with => 'supasikrit') @session.click_button('awesome') - YAML.load(@session.body)['password'].should == 'supasikrit' + extract_results(@session)['password'].should == 'supasikrit' end it "should fill in a password field by label" do pending "Culerity doesn't like password fields for some reason" if @session.mode == :culerity @session.fill_in('Password', :with => 'supasikrit') @session.click_button('awesome') - YAML.load(@session.body)['password'].should == 'supasikrit' + extract_results(@session)['password'].should == 'supasikrit' end end @@ -200,13 +203,13 @@ shared_examples_for "session" do it "should choose a radio button by id" do @session.choose("gender_male") @session.click_button('awesome') - YAML.load(@session.body)['gender'].should == 'male' + extract_results(@session)['gender'].should == 'male' end it "should choose a radio button by label" do @session.choose("Both") @session.click_button('awesome') - YAML.load(@session.body)['gender'].should == 'both' + extract_results(@session)['gender'].should == 'both' end end @@ -219,7 +222,7 @@ shared_examples_for "session" do pending "Culerity doesn't like hidden fields for some reason" if @session.mode == :culerity @session.set_hidden_field("form_token", :to => 'test567') @session.click_button('awesome') - YAML.load(@session.body)['token'].should == 'test567' + extract_results(@session)['token'].should == 'test567' end end @@ -231,13 +234,13 @@ shared_examples_for "session" do it "should check a checkbox by id" do @session.check("form_pets_cat") @session.click_button('awesome') - YAML.load(@session.body)['pets'].should include('dog', 'cat', 'hamster') + extract_results(@session)['pets'].should include('dog', 'cat', 'hamster') end it "should check a checkbox by label" do @session.check("Cat") @session.click_button('awesome') - YAML.load(@session.body)['pets'].should include('dog', 'cat', 'hamster') + extract_results(@session)['pets'].should include('dog', 'cat', 'hamster') end end @@ -250,16 +253,16 @@ shared_examples_for "session" do pending "Culerity doesn't seem to uncheck this" if @session.mode == :culerity @session.uncheck("form_pets_hamster") @session.click_button('awesome') - YAML.load(@session.body)['pets'].should include('dog') - YAML.load(@session.body)['pets'].should_not include('hamster') + extract_results(@session)['pets'].should include('dog') + extract_results(@session)['pets'].should_not include('hamster') end it "should uncheck a checkbox by label" do pending "Culerity doesn't seem to uncheck this" if @session.mode == :culerity @session.uncheck("Hamster") @session.click_button('awesome') - YAML.load(@session.body)['pets'].should include('dog') - YAML.load(@session.body)['pets'].should_not include('hamster') + extract_results(@session)['pets'].should include('dog') + extract_results(@session)['pets'].should_not include('hamster') end end @@ -271,13 +274,13 @@ shared_examples_for "session" do it "should select an option from a select box by id" do @session.select("Finish", :from => 'form_locale') @session.click_button('awesome') - YAML.load(@session.body)['locale'].should == 'fi' + extract_results(@session)['locale'].should == 'fi' end it "should select an option from a select box by label" do @session.select("Finish", :from => 'Locale') @session.click_button('awesome') - YAML.load(@session.body)['locale'].should == 'fi' + extract_results(@session)['locale'].should == 'fi' end end @@ -305,27 +308,31 @@ shared_examples_for "session" do it "should set a file path by id" do @session.attach_file "form_image", __FILE__ @session.click_button('awesome') - YAML.load(@session.body)['image'].should == File.basename(__FILE__) + extract_results(@session)['image'].should == File.basename(__FILE__) end it "should set a file path by label" do @session.attach_file "Image", __FILE__ @session.click_button('awesome') - YAML.load(@session.body)['image'].should == File.basename(__FILE__) + extract_results(@session)['image'].should == File.basename(__FILE__) end end context "with multipart form" do + before do + @test_file_path = File.expand_path('fixtures/test_file.txt', File.dirname(__FILE__)) + end + it "should set a file path by id" do - @session.attach_file "form_document", __FILE__ + @session.attach_file "form_document", @test_file_path @session.click_button('Upload') - @session.body.should == File.read(__FILE__) + @session.body.should include(File.read(@test_file_path)) end it "should set a file path by label" do - @session.attach_file "Document", __FILE__ + @session.attach_file "Document", @test_file_path @session.click_button('Upload') - @session.body.should == File.read(__FILE__) + @session.body.should include(File.read(@test_file_path)) end end diff --git a/spec/test_app.rb b/spec/test_app.rb index 5de5e147..00adfc5a 100644 --- a/spec/test_app.rb +++ b/spec/test_app.rb @@ -46,7 +46,7 @@ class TestApp < Sinatra::Base end post '/form' do - params[:form].to_yaml + '
' + params[:form].to_yaml + '
' end post '/upload' do