langalex-culerity 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +86 -0
- data/VERSION.yml +4 -0
- data/generators/culerity/culerity_generator.rb +20 -0
- data/generators/culerity/templates/common_celerity.rb +70 -0
- data/lib/culerity/celerity_server.rb +46 -0
- data/lib/culerity/remote_browser_proxy.rb +15 -0
- data/lib/culerity/remote_object_proxy.rb +33 -0
- data/lib/culerity.rb +15 -0
- data/spec/celerity_server_spec.rb +70 -0
- data/spec/remote_browser_proxy_spec.rb +17 -0
- data/spec/remote_object_proxy_spec.rb +31 -0
- data/spec/spec_helper.rb +7 -0
- metadata +85 -0
data/README.textile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
h2. Introduction
|
2
|
+
|
3
|
+
Culerity integrates Cucumber and Celerity in order to test your application's full stack.
|
4
|
+
|
5
|
+
Culerity lets you:
|
6
|
+
* 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
|
7
|
+
* 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
|
8
|
+
* reuse existing Webrat-Style step definitions
|
9
|
+
|
10
|
+
h2. Getting Started
|
11
|
+
|
12
|
+
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.
|
13
|
+
|
14
|
+
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...
|
15
|
+
|
16
|
+
export PATH=$HOME/jruby/bin:$PATH
|
17
|
+
|
18
|
+
... or by creating a symlink from your bin directory:
|
19
|
+
|
20
|
+
ln -s $HOME/jruby/bin/jruby /usr/bin/jruby
|
21
|
+
|
22
|
+
Next install the celerity gem for JRuby:
|
23
|
+
|
24
|
+
jruby -S gem install celerity
|
25
|
+
|
26
|
+
Now (assuming you have a Rails application set up already) install Culerity as a Rails Plugin:
|
27
|
+
|
28
|
+
cd RAILS_ROOT
|
29
|
+
git clone git://github.com/langalex/culerity.git
|
30
|
+
|
31
|
+
or as a gem: (definitely preferred)
|
32
|
+
|
33
|
+
gem install langalex-culerity
|
34
|
+
|
35
|
+
And add the culerity gem to your environment.rb:
|
36
|
+
|
37
|
+
config.gem 'langalex-culerity', :lib => 'culerity', :version => '0.1', :source => 'http://gems.github.com'
|
38
|
+
|
39
|
+
Run the RSpec, Cucumber and Culerity generators:
|
40
|
+
|
41
|
+
cd RAILS_ROOT
|
42
|
+
script/generate rspec
|
43
|
+
script/generate cucumber
|
44
|
+
script/generate culerity
|
45
|
+
|
46
|
+
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.
|
47
|
+
|
48
|
+
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.
|
49
|
+
|
50
|
+
NOTE: For now this server has to run on port 80 because of some problem with redirects losing the port information.
|
51
|
+
|
52
|
+
|
53
|
+
sudo script/server -p 80 -e test
|
54
|
+
cucumber features/my_feature.feature
|
55
|
+
|
56
|
+
h2. How does it work
|
57
|
+
|
58
|
+
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.
|
59
|
+
|
60
|
+
h2. Troubleshooting
|
61
|
+
|
62
|
+
I get a broken pipe error:
|
63
|
+
* make sure JRuby is installed and in your path: running _jruby -v_ should not produce an error
|
64
|
+
|
65
|
+
I get _Connection Refused_ errors
|
66
|
+
* make sure you have started a server in the test environment that runs on port 80
|
67
|
+
|
68
|
+
|
69
|
+
h2. Links to Celerity documentation
|
70
|
+
|
71
|
+
* "How to select elements":http://celerity.rubyforge.org/yard/Celerity/Container.html
|
72
|
+
* "FAQ":http://celerity.rubyforge.org/wiki/wiki.pl
|
73
|
+
* "Tutorial":http://celerity.rubyforge.org/wiki/wiki.pl?GettingStarted
|
74
|
+
* "API docs":http://celerity.rubyforge.org/yard/
|
75
|
+
|
76
|
+
h2. Links
|
77
|
+
|
78
|
+
* "cucumber":http://github.com/aslakhellesoy/cucumber/wikis
|
79
|
+
* "celerity":http://celerity.rubyforge.org
|
80
|
+
* "jruby":http://jruby.codehaus.org
|
81
|
+
* "rspec":http://rspec.info
|
82
|
+
* "htmlunit":http://htmlunit.sourceforge.net/
|
83
|
+
|
84
|
+
h2. Contact
|
85
|
+
|
86
|
+
Written 2009 by Alexander Lang, contact alex[at]upstream-berlin.com or http://github.com/langalex, released under the MIT license
|
data/VERSION.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class CulerityGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def initialize(runtime_args, runtime_options = {})
|
4
|
+
Dir.mkdir('features/step_definitions') unless File.directory?('features/step_definitions')
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def manifest
|
9
|
+
record do |m|
|
10
|
+
m.template 'common_celerity.rb', 'features/step_definitions/common_celerity.rb'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def banner
|
17
|
+
"Usage: #{$0} culerity"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
Before do
|
2
|
+
@server = Culerity::run_server
|
3
|
+
@browser = Culerity::RemoteBrowserProxy.new @server
|
4
|
+
@host = 'http://localhost'
|
5
|
+
end
|
6
|
+
|
7
|
+
After do
|
8
|
+
@browser.close
|
9
|
+
@browser.exit
|
10
|
+
@server.close
|
11
|
+
end
|
12
|
+
|
13
|
+
When /I press "(.*)"/ do |button|
|
14
|
+
@browser.button(:text, button).click
|
15
|
+
assert_successful_response
|
16
|
+
end
|
17
|
+
|
18
|
+
When /I follow "(.*)"/ do |link|
|
19
|
+
@browser.link(:text, /#{link}/).click
|
20
|
+
assert_successful_response
|
21
|
+
end
|
22
|
+
|
23
|
+
When /I fill in "(.*)" for "(.*)"/ do |value, field|
|
24
|
+
@browser.text_field(:id, find_label(field).for).set(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
When /I check "(.*)"/ do |field|
|
28
|
+
@browser.check_box(:id, find_label(field).for).set(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
When /^I uncheck "(.*)"$/ do |field|
|
32
|
+
@browser.check_box(:id, find_label(field).for).set(false)
|
33
|
+
end
|
34
|
+
|
35
|
+
When /I choose "(.*)"/ do |field|
|
36
|
+
@browser.radio(:id, find_label(field).for).set(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
When /I go to "(.*)"/ do |path|
|
40
|
+
@browser.goto @host + path
|
41
|
+
assert_successful_response
|
42
|
+
end
|
43
|
+
|
44
|
+
When "I wait for the AJAX call to finish" do
|
45
|
+
@browser.page.getEnclosingWindow().getThreadManager().joinAll(10000)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
Then /I should see "(.*)"/ do |text|
|
50
|
+
@browser.html.should =~ /#{text}/m
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /I should not see "(.*)"/ do |text|
|
54
|
+
@browser.html.should_not =~ /#{text}/m
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_label(text)
|
58
|
+
@browser.label :text, text
|
59
|
+
end
|
60
|
+
|
61
|
+
def assert_successful_response
|
62
|
+
status = @browser.page.web_response.status_code
|
63
|
+
if(status == 302 || status == 301)
|
64
|
+
location = @browser.page.web_response.get_response_header_value('Location')
|
65
|
+
puts "Being redirected to #{location}"
|
66
|
+
@browser.goto location
|
67
|
+
elsif status != 200
|
68
|
+
raise "Brower returned Response Code #{@browser.page.web_response.status_code}"
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'celerity'
|
3
|
+
|
4
|
+
|
5
|
+
module Culerity
|
6
|
+
class CelerityServer
|
7
|
+
|
8
|
+
def initialize(_in, _out)
|
9
|
+
@browser = Celerity::Browser.new
|
10
|
+
@proxies = {}
|
11
|
+
|
12
|
+
while(true)
|
13
|
+
call = eval _in.gets.to_s.strip
|
14
|
+
return if call == ["_exit_"]
|
15
|
+
unless call.nil?
|
16
|
+
begin
|
17
|
+
result = target(call.first).send call[1], *call[2..-1]
|
18
|
+
_out << "[:return, #{proxify result}]\n"
|
19
|
+
rescue => e
|
20
|
+
_out << "[:exception, \"#{e.class}\", #{e.message.inspect}]\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def target(object_id)
|
30
|
+
if object_id == 'browser'
|
31
|
+
@browser
|
32
|
+
else
|
33
|
+
@proxies[object_id]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def proxify(result)
|
38
|
+
if [String, TrueClass, FalseClass, Fixnum, Float, NilClass].include?(result.class)
|
39
|
+
result.inspect
|
40
|
+
else
|
41
|
+
@proxies[result.object_id] = result
|
42
|
+
"Culerity::RemoteObjectProxy.new(#{result.object_id}, @io)"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Culerity
|
2
|
+
|
3
|
+
class RemoteObjectProxy
|
4
|
+
def initialize(remote_object_id, io)
|
5
|
+
@remote_object_id = remote_object_id
|
6
|
+
@io = io
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(name, *args)
|
10
|
+
@io << "[#{remote_object_id}, \"#{name}\", #{args.map{|a| a.inspect}.join(', ')}]\n"
|
11
|
+
process_result @io.gets.to_s.strip
|
12
|
+
end
|
13
|
+
|
14
|
+
def exit
|
15
|
+
@io << '["_exit_"]'
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def process_result(result)
|
21
|
+
res = eval result
|
22
|
+
if res.first == :return
|
23
|
+
res[1]
|
24
|
+
elsif res.first == :exception
|
25
|
+
raise "#{res[1]}: #{res[2]}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def remote_object_id
|
30
|
+
@remote_object_id
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/culerity.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/culerity/remote_object_proxy'
|
2
|
+
require File.dirname(__FILE__) + '/culerity/remote_browser_proxy'
|
3
|
+
|
4
|
+
module Culerity
|
5
|
+
|
6
|
+
def self.run_server
|
7
|
+
IO.popen("jruby #{__FILE__}", 'r+')
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
if __FILE__ == $0
|
13
|
+
require File.dirname(__FILE__) + '/culerity/celerity_server'
|
14
|
+
Culerity::CelerityServer.new STDIN, STDOUT
|
15
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Culerity::CelerityServer do
|
4
|
+
before(:each) do
|
5
|
+
@browser = stub 'browser'
|
6
|
+
Celerity::Browser.stub!(:new).and_return(@browser)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should pass the method call to the selerity browser" do
|
10
|
+
@browser.should_receive(:goto).with('/homepage')
|
11
|
+
_in = stub 'in'
|
12
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
|
13
|
+
_out = stub 'out', :<< => nil
|
14
|
+
Culerity::CelerityServer.new(_in, _out)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should send back the return value of the call" do
|
18
|
+
@browser.stub!(:goto).and_return(true)
|
19
|
+
_in = stub 'in'
|
20
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
|
21
|
+
_out = stub 'out'
|
22
|
+
_out.should_receive(:<<).with("[:return, true]\n")
|
23
|
+
Culerity::CelerityServer.new(_in, _out)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should ignore empty inputs" do
|
27
|
+
_in = stub 'in'
|
28
|
+
_in.stub!(:gets).and_return("\n", "[\"_exit_\"]\n")
|
29
|
+
_out = stub 'out'
|
30
|
+
_out.should_not_receive(:<<)
|
31
|
+
Culerity::CelerityServer.new(_in, _out)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should send back a proxy if the return value is not a string, number, nil or boolean" do
|
35
|
+
@browser.stub!(:goto).and_return(stub('123', :object_id => 456))
|
36
|
+
_in = stub 'in'
|
37
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
|
38
|
+
_out = stub 'out'
|
39
|
+
_out.should_receive(:<<).with("[:return, Culerity::RemoteObjectProxy.new(456, @io)]\n")
|
40
|
+
Culerity::CelerityServer.new(_in, _out)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should pass the method call to a proxy" do
|
44
|
+
proxy = stub('123', :object_id => 456)
|
45
|
+
@browser.stub!(:goto).and_return(proxy)
|
46
|
+
_in = stub 'in'
|
47
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[456, \"goto_2\", \"1\"]", "[\"_exit_\"]\n")
|
48
|
+
_out = stub 'out', :<< => nil
|
49
|
+
proxy.should_receive(:goto_2).with('1')
|
50
|
+
Culerity::CelerityServer.new(_in, _out)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should pass multiple method calls" do
|
54
|
+
@browser.should_receive(:goto).with('/homepage')
|
55
|
+
@browser.should_receive(:goto).with('/page2')
|
56
|
+
_in = stub 'in'
|
57
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"browser\", \"goto\", \"/page2\"]\n", "[\"_exit_\"]\n")
|
58
|
+
_out = stub 'out', :<< => nil
|
59
|
+
Culerity::CelerityServer.new(_in, _out)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return an exception" do
|
63
|
+
@browser.stub!(:goto).and_raise(RuntimeError.new('test exception'))
|
64
|
+
_in = stub 'in'
|
65
|
+
_in.stub!(:gets).and_return("[\"browser\", \"goto\", \"/homepage\"]\n", "[\"_exit_\"]\n")
|
66
|
+
_out = stub 'out'
|
67
|
+
_out.should_receive(:<<).with("[:exception, \"RuntimeError\", \"test exception\"]\n")
|
68
|
+
Culerity::CelerityServer.new(_in, _out)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Culerity::RemoteBrowserProxy do
|
4
|
+
it "should send the serialized method call to the output" do
|
5
|
+
io = stub 'io', :gets => '[return, :okay]'
|
6
|
+
io.should_receive(:<<).with("[\"browser\", \"goto\", \"/homepage\"]\n")
|
7
|
+
proxy = Culerity::RemoteBrowserProxy.new io
|
8
|
+
proxy.goto '/homepage'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return the deserialized return value" do
|
12
|
+
io = stub 'io', :gets => "[:return, :okay]\n", :<< => nil
|
13
|
+
proxy = Culerity::RemoteBrowserProxy.new io
|
14
|
+
proxy.goto.should == :okay
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Culerity::RemoteObjectProxy do
|
4
|
+
it "should send the serialized method call to the output" do
|
5
|
+
io = stub 'io', :gets => '[:return]'
|
6
|
+
io.should_receive(:<<).with("[345, \"goto\", \"/homepage\"]\n")
|
7
|
+
proxy = Culerity::RemoteObjectProxy.new 345, io
|
8
|
+
proxy.goto '/homepage'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return the deserialized return value" do
|
12
|
+
io = stub 'io', :gets => "[:return, :okay]\n", :<< => nil
|
13
|
+
proxy = Culerity::RemoteObjectProxy.new 345, io
|
14
|
+
proxy.goto.should == :okay
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise the received exception" do
|
18
|
+
io = stub 'io', :gets => "[:exception, \"RuntimeError\", \"test exception\"]", :<< => nil
|
19
|
+
proxy = Culerity::RemoteObjectProxy.new 345, io
|
20
|
+
lambda {
|
21
|
+
proxy.goto '/home'
|
22
|
+
}.should raise_error(RuntimeError, 'RuntimeError: test exception')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should send exit" do
|
26
|
+
io = stub 'io', :gets => '[:return]'
|
27
|
+
io.should_receive(:<<).with('["_exit_"]')
|
28
|
+
proxy = Culerity::RemoteObjectProxy.new 345, io
|
29
|
+
proxy.exit
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: langalex-culerity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Lang
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-25 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: cucumber
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rspec
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
description: Culerity integrates Cucumber and Celerity in order to test your application's full stack.
|
34
|
+
email: alex@upstream-berlin.com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- README.textile
|
43
|
+
- VERSION.yml
|
44
|
+
- generators/culerity
|
45
|
+
- generators/culerity/culerity_generator.rb
|
46
|
+
- generators/culerity/templates
|
47
|
+
- generators/culerity/templates/common_celerity.rb
|
48
|
+
- lib/culerity
|
49
|
+
- lib/culerity/celerity_server.rb
|
50
|
+
- lib/culerity/remote_browser_proxy.rb
|
51
|
+
- lib/culerity/remote_object_proxy.rb
|
52
|
+
- lib/culerity.rb
|
53
|
+
- spec/celerity_server_spec.rb
|
54
|
+
- spec/remote_browser_proxy_spec.rb
|
55
|
+
- spec/remote_object_proxy_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/langalex/culerity
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --inline-source
|
62
|
+
- --charset=UTF-8
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.2.0
|
81
|
+
signing_key:
|
82
|
+
specification_version: 2
|
83
|
+
summary: Culerity integrates Cucumber and Celerity in order to test your application's full stack.
|
84
|
+
test_files: []
|
85
|
+
|