Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit ba9fe76a75eb341b34893554722b5017d543b949 0 parents
Alexander Lang langalex authored
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Alexander Lang
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+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.
82 README.textile
@@ -0,0 +1,82 @@
+h2. Introduction
+
+Culerity integrates Cucumber and Celerity in order to test your application's full stack.
+
+Culerity lets you:
+* run Celerity from within Cucumber which allows you to test the full stack of your Rails (or other web) application from Database to in browser JavaScript
+* run your application in any Ruby (like MRI 1.8.6) while Celerity runs in JRuby so you can still use gems/plugins that would not work with JRuby
+* reuse existing Webrat-Style step definitions
+
+h2. Getting Started
+
+The following guide is written for a Rails application (tested with 2.2.2) but Culerity should work with any other Web Framework that is supported by Cucumber.
+
+First download JRuby and unpack it to some location, for example $HOME/jruby. Make sure that the jruby executable is in your path. You can do this by either setting your PATH accordingly...
+
+ export PATH=$HOME/jruby/bin:$PATH
+
+... or by creating a symlink from your bin directory:
+
+ln -s $HOME/jruby/bin/jruby /usr/bin/jruby
+
+Now (assuming you already have a Rails application set up already) install Culerity as a Rails Plugin:
+
+ cd RAILS_ROOT
+ git clone
+
+or as a gem:
+
+ gem install langalex-culerity
+
+And add the culerity gem to your environment.rb:
+
+ config.gem 'langalex-culerity', :lib => 'culerity', :version => '0.1', :source => 'http://gems.github.com'
+
+Run the RSpec, Cucumber and Culerity generators:
+
+ cd RAILS_ROOT
+ script/generate rspec
+ script/generate cucumber
+ script/generate culerity
+
+This creates the features folder and a file common_celerity.rb into your application. This file contains step definitions for basic interactions like clicking links or filling out forms.
+
+After you have written a first feature you can run it just like you would run a standard cucumber feature. The only difference is that you have to start a web server (e.g. mongrel) with the test environment enabled beforehand.
+
+NOTE: For now this server has to run on port 80 because of some problem with redirects losing the port information.
+
+
+ sudo script/server -p 80 -e test
+ cucumber features/my_feature.feature
+
+h2. How does it work
+
+While Celerity is based on Java and requires JRuby to run, with Culerity you can still run your tests in your own Ruby Environment. When you run your features a separate JRuby process for Celerity is spawned and all Celerity Commands are redirected to this other process.
+
+h2. Troubleshooting
+
+I get a broken pipe error:
+* make sure JRuby is installed and in your path: running _jruby -v_ should not produce an error
+
+I get _Connection Refused_ errors
+* make sure you have started a server in the test environment that runs on port 80
+
+
+h2. Links to Celerity documentation
+
+* "How to select elements":http://celerity.rubyforge.org/yard/Celerity/Container.html
+* "FAQ":http://celerity.rubyforge.org/wiki/wiki.pl
+* "Tutorial":http://celerity.rubyforge.org/wiki/wiki.pl?GettingStarted
+* "API docs":http://celerity.rubyforge.org/yard/
+
+h2. Links
+
+[cucumber]http://github.com/aslakhellesoy/cucumber/wikis
+[celerity]http://celerity.rubyforge.org
+[jruby]http://jruby.codehaus.org
+[rspec]http://rspec.info
+[htmlunit]http://htmlunit.sourceforge.net/
+
+h2. Contact
+
+Written 2009 by Alexander Lang, contact alex[at]upstream-berlin.com or http://github.com/langalex, released under the MIT license
20 generators/culerity/culerity_generator.rb
@@ -0,0 +1,20 @@
+class CulerityGenerator < Rails::Generator::Base
+
+ def initialize(runtime_args, runtime_options = {})
+ Dir.mkdir('features/step_definitions') unless File.directory?('features/step_definitions')
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.template 'common_celerity.rb', 'features/step_definitions/common_celerity.rb'
+ end
+ end
+
+protected
+
+ def banner
+ "Usage: #{$0} culerity"
+ end
+
+end
70 generators/culerity/templates/common_celerity.rb
@@ -0,0 +1,70 @@
+Before do
+ @server = Culerity::run_server
+ @browser = Culerity::RemoteBrowserProxy.new @server
+ @host = 'http://localhost'
+end
+
+After do
+ @browser.close
+ @browser.exit
+ @server.close
+end
+
+When /I press "(.*)"/ do |button|
+ @browser.button(:text, button).click
+ assert_successful_response
+end
+
+When /I follow "(.*)"/ do |link|
+ @browser.link(:text, /#{link}/).click
+ assert_successful_response
+end
+
+When /I fill in "(.*)" for "(.*)"/ do |value, field|
+ @browser.text_field(:id, find_label(field).for).set(value)
+end
+
+When /I check "(.*)"/ do |field|
+ @browser.check_box(:id, find_label(field).for).set(true)
+end
+
+When /^I uncheck "(.*)"$/ do |field|
+ @browser.check_box(:id, find_label(field).for).set(false)
+end
+
+When /I choose "(.*)"/ do |field|
+ @browser.radio(:id, find_label(field).for).set(true)
+end
+
+When /I go to "(.*)"/ do |path|
+ @browser.goto @host + path
+ assert_successful_response
+end
+
+When "I wait for the AJAX call to finish" do
+ @browser.page.getEnclosingWindow().getThreadManager().joinAll(10000)
+end
+
+
+Then /I should see "(.*)"/ do |text|
+ @browser.html.should =~ /#{text}/m
+end
+
+Then /I should not see "(.*)"/ do |text|
+ @browser.html.should_not =~ /#{text}/m
+end
+
+def find_label(text)
+ @browser.label :text, text
+end
+
+def assert_successful_response
+ status = @browser.page.web_response.status_code
+ if(status == 302 || status == 301)
+ location = @browser.page.web_response.get_response_header_value('Location')
+ puts "Being redirected to #{location}"
+ @browser.goto location
+ elsif status != 200
+ raise "Brower returned Response Code #{@browser.page.web_response.status_code}"
+ end
+end
1  init.rb
@@ -0,0 +1 @@
+require 'rails/init'
15 lib/culerity.rb
@@ -0,0 +1,15 @@
+require File.dirname(__FILE__) + '/culerity/remote_object_proxy'
+require File.dirname(__FILE__) + '/culerity/remote_browser_proxy'
+
+module Culerity
+
+ def self.run_server
+ IO.popen("jruby #{__FILE__}", 'r+')
+ end
+
+end
+
+if __FILE__ == $0
+ require File.dirname(__FILE__) + '/culerity/celerity_server'
+ Culerity::CelerityServer.new STDIN, STDOUT
+end
46 lib/culerity/celerity_server.rb
@@ -0,0 +1,46 @@
+require 'rubygems'
+require 'celerity'
+
+
+module Culerity
+ class CelerityServer
+
+ def initialize(_in, _out)
+ @browser = Celerity::Browser.new
+ @proxies = {}
+
+ while(true)
+ call = eval _in.gets.to_s.strip
+ return if call == ["_exit_"]
+ unless call.nil?
+ begin
+ result = target(call.first).send call[1], *call[2..-1]
+ _out << "[:return, #{proxify result}]\n"
+ rescue => e
+ _out << "[:exception, \"#{e.class}\", #{e.message.inspect}]\n"
+ end
+ end
+ end
+
+ end
+
+ private
+
+ def target(object_id)
+ if object_id == 'browser'
+ @browser
+ else
+ @proxies[object_id]
+ end
+ end
+
+ def proxify(result)
+ if [String, TrueClass, FalseClass, Fixnum, Float, NilClass].include?(result.class)
+ result.inspect
+ else
+ @proxies[result.object_id] = result
+ "Culerity::RemoteObjectProxy.new(#{result.object_id}, @io)"
+ end
+ end
+ end
+end
15 lib/culerity/remote_browser_proxy.rb
@@ -0,0 +1,15 @@
+module Culerity
+
+ class RemoteBrowserProxy < RemoteObjectProxy
+ def initialize(io)
+ @io = io
+ end
+
+ private
+
+ def remote_object_id
+ '"browser"'
+ end
+ end
+
+end
33 lib/culerity/remote_object_proxy.rb
@@ -0,0 +1,33 @@
+module Culerity
+
+ class RemoteObjectProxy
+ def initialize(remote_object_id, io)
+ @remote_object_id = remote_object_id
+ @io = io
+ end
+
+ def method_missing(name, *args)
+ @io << "[#{remote_object_id}, \"#{name}\", #{args.map{|a| a.inspect}.join(', ')}]\n"
+ process_result @io.gets.to_s.strip
+ end
+
+ def exit
+ @io << '["_exit_"]'
+ end
+
+ private
+
+ def process_result(result)
+ res = eval result
+ if res.first == :return
+ res[1]
+ elsif res.first == :exception
+ raise "#{res[1]}: #{res[2]}"
+ end
+ end
+
+ def remote_object_id
+ @remote_object_id
+ end
+ end
+end
1  rails/init.rb
@@ -0,0 +1 @@
+require 'culerity'
70 spec/celerity_server_spec.rb
@@ -0,0 +1,70 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Culerity::CelerityServer do
+ before(:each) do
+ @browser = stub 'browser'
+ Celerity::Browser.stub!(:new).and_return(@browser)
+ end
+
+ it "should pass the method call to the selerity browser" do
+ @browser.should_receive(:goto).with('/homepage')
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
+ _out = stub 'out', :<< => nil
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should send back the return value of the call" do
+ @browser.stub!(:goto).and_return(true)
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
+ _out = stub 'out'
+ _out.should_receive(:<<).with("[:return, true]\n")
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should ignore empty inputs" do
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("\n", "[\"_exit_\"]\n")
+ _out = stub 'out'
+ _out.should_not_receive(:<<)
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should send back a proxy if the return value is not a string, number, nil or boolean" do
+ @browser.stub!(:goto).and_return(stub('123', :object_id => 456))
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
+ _out = stub 'out'
+ _out.should_receive(:<<).with("[:return, Culerity::RemoteObjectProxy.new(456, @io)]\n")
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should pass the method call to a proxy" do
+ proxy = stub('123', :object_id => 456)
+ @browser.stub!(:goto).and_return(proxy)
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[456, \"goto_2\", \"1\"]", "[\"_exit_\"]\n")
+ _out = stub 'out', :<< => nil
+ proxy.should_receive(:goto_2).with('1')
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should pass multiple method calls" do
+ @browser.should_receive(:goto).with('/homepage')
+ @browser.should_receive(:goto).with('/page2')
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"browser\", \"goto\", \"/page2\"]\n", "[\"_exit_\"]\n")
+ _out = stub 'out', :<< => nil
+ Culerity::CelerityServer.new(_in, _out)
+ end
+
+ it "should return an exception" do
+ @browser.stub!(:goto).and_raise(RuntimeError.new('test exception'))
+ _in = stub 'in'
+ _in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
+ _out = stub 'out'
+ _out.should_receive(:<<).with("[:exception, \"RuntimeError\", \"test exception\"]\n")
+ Culerity::CelerityServer.new(_in, _out)
+ end
+end
17 spec/remote_browser_proxy_spec.rb
@@ -0,0 +1,17 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Culerity::RemoteBrowserProxy do
+ it "should send the serialized method call to the output" do
+ io = stub 'io', :gets => '[return, :okay]'
+ io.should_receive(:<<).with("[\"browser\", \"goto\", \"/homepage\"]\n")
+ proxy = Culerity::RemoteBrowserProxy.new io
+ proxy.goto '/homepage'
+ end
+
+ it "should return the deserialized return value" do
+ io = stub 'io', :gets => "[:return, :okay]\n", :<< => nil
+ proxy = Culerity::RemoteBrowserProxy.new io
+ proxy.goto.should == :okay
+ end
+
+end
31 spec/remote_object_proxy_spec.rb
@@ -0,0 +1,31 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe Culerity::RemoteObjectProxy do
+ it "should send the serialized method call to the output" do
+ io = stub 'io', :gets => '[:return]'
+ io.should_receive(:<<).with("[345, \"goto\", \"/homepage\"]\n")
+ proxy = Culerity::RemoteObjectProxy.new 345, io
+ proxy.goto '/homepage'
+ end
+
+ it "should return the deserialized return value" do
+ io = stub 'io', :gets => "[:return, :okay]\n", :<< => nil
+ proxy = Culerity::RemoteObjectProxy.new 345, io
+ proxy.goto.should == :okay
+ end
+
+ it "should raise the received exception" do
+ io = stub 'io', :gets => "[:exception, \"RuntimeError\", \"test exception\"]", :<< => nil
+ proxy = Culerity::RemoteObjectProxy.new 345, io
+ lambda {
+ proxy.goto '/home'
+ }.should raise_error(RuntimeError, 'RuntimeError: test exception')
+ end
+
+ it "should send exit" do
+ io = stub 'io', :gets => '[:return]'
+ io.should_receive(:<<).with('["_exit_"]')
+ proxy = Culerity::RemoteObjectProxy.new 345, io
+ proxy.exit
+ end
+end
7 spec/spec_helper.rb
@@ -0,0 +1,7 @@
+if RUBY_PLATFORM != 'java'
+ puts "You need JRuby to run these specs"
+ exit -1
+end
+
+require File.dirname(__FILE__) + '/../lib/culerity'
+require File.dirname(__FILE__) + '/../lib/culerity/celerity_server'
Please sign in to comment.
Something went wrong with that request. Please try again.