langalex-culerity 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|