ryansch-capybara-mechanize 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +68 -0
- data/lib/capybara/mechanize.rb +24 -0
- data/lib/capybara/mechanize/browser.rb +172 -0
- data/lib/capybara/mechanize/cucumber.rb +5 -0
- data/lib/capybara/mechanize/driver.rb +19 -0
- data/lib/capybara/mechanize/form.rb +65 -0
- data/lib/capybara/mechanize/node.rb +10 -0
- data/lib/capybara/mechanize/version.rb +5 -0
- data/lib/capybara/spec/extended_test_app.rb +65 -0
- data/spec/driver/mechanize_driver_spec.rb +215 -0
- data/spec/driver/remote_mechanize_driver_spec.rb +46 -0
- data/spec/session/mechanize_spec.rb +95 -0
- data/spec/session/remote_mechanize_spec.rb +103 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/disable_external_tests.rb +20 -0
- data/spec/support/extended_test_app_setup.rb +30 -0
- metadata +103 -0
data/README.mdown
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
Capybara-mechanize
|
2
|
+
==================
|
3
|
+
|
4
|
+
This gems makes it possible to use Capybara for (partially) remote testing. It inherits most functionality from the RackTest driver and only uses [Mechanize](http://github.com/tenderlove/mechanize) for remote requests.
|
5
|
+
|
6
|
+
It is currently in use to test the integration between a Rails application and Twitter authorization and sharing.
|
7
|
+
|
8
|
+
This gem is a [Capybara](http://github.com/jnicklas/capybara) extension. I have been inspired by the Capybara driver and some earlier efforts for a Mechanize driver.
|
9
|
+
|
10
|
+
Thanks to [Pinkelstar](http://www.pinkelstar.com) for giving me the time and the need to develop this gem.
|
11
|
+
|
12
|
+
### Installation
|
13
|
+
|
14
|
+
gem install capybara-mechanize
|
15
|
+
|
16
|
+
### Usage without Cucumber
|
17
|
+
|
18
|
+
require 'capybara/mechanize'
|
19
|
+
|
20
|
+
### Usage with Cucumber and tags
|
21
|
+
|
22
|
+
A @mechanize tag is added to your hooks when you add the following line to your env.rb
|
23
|
+
|
24
|
+
require 'capybara/mechanize/cucumber'
|
25
|
+
|
26
|
+
The following scenario will then be using the Mechanize driver
|
27
|
+
|
28
|
+
@mechanize
|
29
|
+
Scenario: do something remote
|
30
|
+
When I click the remote link
|
31
|
+
|
32
|
+
### Remote testing
|
33
|
+
|
34
|
+
When you want to use this driver to test a remote application. You have to set the app_host:
|
35
|
+
|
36
|
+
Capybara.app_host = "http://www.yourapp.com"
|
37
|
+
|
38
|
+
Note that I haven't tested this case for my self yet. The Capybara tests pass for this situation though so it should work! Please provide me with feedback if it doesn't.
|
39
|
+
|
40
|
+
## Running tests
|
41
|
+
|
42
|
+
Run bundler
|
43
|
+
|
44
|
+
bundle install
|
45
|
+
|
46
|
+
Then you are ready to run the test like so
|
47
|
+
|
48
|
+
bundle exec rake spec
|
49
|
+
|
50
|
+
Todo
|
51
|
+
----
|
52
|
+
* Make the last 12 failing remote session spec pass, see remote_mechanize_spec and uncomment one line there to see them fail
|
53
|
+
* Test this driver with non-rack/non-ruby projects
|
54
|
+
|
55
|
+
Note on Patches/Pull Requests
|
56
|
+
-----------------------------
|
57
|
+
|
58
|
+
* Fork the project.
|
59
|
+
* Make your feature addition or bug fix.
|
60
|
+
* Add tests for it. This is important so I don't break it in a
|
61
|
+
future version unintentionally.
|
62
|
+
* Commit, do not mess with rakefile, version, or history.
|
63
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
64
|
+
* Send me a pull request. Bonus points for topic branches.
|
65
|
+
|
66
|
+
Copyright
|
67
|
+
---------
|
68
|
+
Copyright (c) 2010-2012 Jeroen van Dijk. See LICENSE for details.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
|
3
|
+
module Capybara::Mechanize
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Host that should be considered local (includes default_host)
|
7
|
+
def local_hosts
|
8
|
+
@local_hosts ||= begin
|
9
|
+
default_host = URI.parse(Capybara.default_host || "").host || Capybara.default_host
|
10
|
+
[default_host].compact
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def local_hosts=(hosts)
|
15
|
+
@local_hosts = hosts
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'capybara/mechanize/driver'
|
21
|
+
|
22
|
+
Capybara.register_driver :mechanize do |app|
|
23
|
+
Capybara::Mechanize::Driver.new(app)
|
24
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'capybara/rack_test/driver'
|
2
|
+
require 'mechanize'
|
3
|
+
require 'capybara/mechanize/node'
|
4
|
+
require 'capybara/mechanize/form'
|
5
|
+
|
6
|
+
class Capybara::Mechanize::Browser < Capybara::RackTest::Browser
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegator :agent, :scheme_handlers
|
10
|
+
def_delegator :agent, :scheme_handlers=
|
11
|
+
|
12
|
+
def initialize(driver)
|
13
|
+
@agent = ::Mechanize.new
|
14
|
+
@agent.redirect_ok = false
|
15
|
+
@agent.user_agent = default_user_agent
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset_host!
|
20
|
+
@last_remote_uri = nil
|
21
|
+
@last_request_remote = nil
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_url
|
26
|
+
last_request_remote? ? remote_response.current_url : super
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_response
|
30
|
+
last_request_remote? ? remote_response : super
|
31
|
+
end
|
32
|
+
|
33
|
+
# For each of these http methods, we want to intercept the method call.
|
34
|
+
# Then we determine if the call is remote or local.
|
35
|
+
# Remote: Handle it with our process_remote_request method.
|
36
|
+
# Local: Register the local request and call super to let RackTest get it.
|
37
|
+
[:get, :post, :put, :delete].each do |method|
|
38
|
+
define_method(method) do |path, params = {}, env = {}, &block|
|
39
|
+
path = @last_path if path.nil? || path.empty?
|
40
|
+
|
41
|
+
if remote?(path)
|
42
|
+
process_remote_request(method, path, params, env, &block)
|
43
|
+
else
|
44
|
+
register_local_request
|
45
|
+
super(path, params, env, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
@last_path = path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def remote?(url)
|
53
|
+
if Capybara.app_host
|
54
|
+
true
|
55
|
+
else
|
56
|
+
host = URI.parse(url).host
|
57
|
+
|
58
|
+
if host.nil?
|
59
|
+
last_request_remote?
|
60
|
+
else
|
61
|
+
!Capybara::Mechanize.local_hosts.include?(host)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def find(selector)
|
67
|
+
dom.xpath(selector).map { |node| Capybara::Mechanize::Node.new(self, node) }
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_reader :agent
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def last_request_remote?
|
75
|
+
!!@last_request_remote
|
76
|
+
end
|
77
|
+
|
78
|
+
def register_local_request
|
79
|
+
@last_remote_uri = nil
|
80
|
+
@last_request_remote = false
|
81
|
+
end
|
82
|
+
|
83
|
+
def remote_request_path
|
84
|
+
@last_remote_uri.nil? ? nil : @last_remote_uri.path
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_path
|
88
|
+
last_request_remote? ? remote_request_path : super
|
89
|
+
end
|
90
|
+
|
91
|
+
def process_remote_request(method, url, attributes, headers)
|
92
|
+
if remote?(url)
|
93
|
+
uri = URI.parse(url)
|
94
|
+
@last_remote_uri = uri
|
95
|
+
url = uri.to_s
|
96
|
+
|
97
|
+
reset_cache!
|
98
|
+
begin
|
99
|
+
if method == :post
|
100
|
+
if attributes.is_a? Mechanize::Form
|
101
|
+
submit_mechanize_form(url, attributes, headers)
|
102
|
+
else
|
103
|
+
@agent.send(method, url, attributes, headers)
|
104
|
+
end
|
105
|
+
elsif method == :get
|
106
|
+
if attributes.is_a? Mechanize::Form
|
107
|
+
submit_mechanize_form(url, attributes, headers)
|
108
|
+
else
|
109
|
+
referer = headers['HTTP_REFERER']
|
110
|
+
@agent.send(method, url, attributes, referer, headers)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
@agent.send(method, url, attributes, headers)
|
114
|
+
end
|
115
|
+
rescue => e
|
116
|
+
raise "Received the following error for a #{method.to_s.upcase} request to #{url}: '#{e.message}'"
|
117
|
+
end
|
118
|
+
@last_request_remote = true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def submit_mechanize_form(url, form, headers)
|
123
|
+
form.action = url
|
124
|
+
@agent.submit(form, nil, headers)
|
125
|
+
end
|
126
|
+
|
127
|
+
def remote_response
|
128
|
+
ResponseProxy.new(@agent.current_page) if @agent.current_page
|
129
|
+
end
|
130
|
+
|
131
|
+
def default_user_agent
|
132
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.853.0 Safari/535.2"
|
133
|
+
end
|
134
|
+
|
135
|
+
class ResponseProxy
|
136
|
+
extend Forwardable
|
137
|
+
|
138
|
+
def_delegator :page, :body
|
139
|
+
|
140
|
+
attr_reader :page
|
141
|
+
|
142
|
+
def initialize(page)
|
143
|
+
@page = page
|
144
|
+
end
|
145
|
+
|
146
|
+
def current_url
|
147
|
+
page.uri.to_s
|
148
|
+
end
|
149
|
+
|
150
|
+
def headers
|
151
|
+
# Hax the content-type contains utf8, so Capybara specs are failing, need to ask mailinglist
|
152
|
+
headers = page.response
|
153
|
+
headers["content-type"].gsub!(';charset=utf-8', '') if headers["content-type"]
|
154
|
+
headers
|
155
|
+
end
|
156
|
+
|
157
|
+
def [](key)
|
158
|
+
headers[key]
|
159
|
+
end
|
160
|
+
|
161
|
+
def status
|
162
|
+
page.code.to_i
|
163
|
+
end
|
164
|
+
|
165
|
+
def redirect?
|
166
|
+
status >= 300 && status < 400
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'capybara/mechanize/browser'
|
2
|
+
|
3
|
+
class Capybara::Mechanize::Driver < Capybara::RackTest::Driver
|
4
|
+
|
5
|
+
def initialize(app, options = {})
|
6
|
+
raise ArgumentError, "mechanize requires a rack application, but none was given" unless app
|
7
|
+
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def remote?(url)
|
12
|
+
browser.remote?(url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def browser
|
16
|
+
@browser ||= Capybara::Mechanize::Browser.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Capybara::Mechanize::Form < Capybara::RackTest::Form
|
2
|
+
|
3
|
+
def params(button)
|
4
|
+
if !use_mechanize?
|
5
|
+
return super
|
6
|
+
end
|
7
|
+
|
8
|
+
node = {}
|
9
|
+
# Create a fake form
|
10
|
+
class << node
|
11
|
+
def search(*args); []; end
|
12
|
+
end
|
13
|
+
node['method'] = method.to_s.upcase
|
14
|
+
|
15
|
+
if self.multipart?
|
16
|
+
node['enctype'] = 'multipart/form-data'
|
17
|
+
else
|
18
|
+
node['enctype'] = 'application/x-www-form-urlencoded'
|
19
|
+
end
|
20
|
+
|
21
|
+
@m_form = Mechanize::Form.new(node, nil, form_referer)
|
22
|
+
|
23
|
+
super
|
24
|
+
|
25
|
+
@m_form
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def merge_param!(params, key, value)
|
31
|
+
if !use_mechanize?
|
32
|
+
return super
|
33
|
+
end
|
34
|
+
|
35
|
+
if value.is_a? NilUploadedFile
|
36
|
+
# Adding a nil value here will result in the form element existing with the empty string as its value.
|
37
|
+
# Instead don't add the form element at all.
|
38
|
+
return params
|
39
|
+
end
|
40
|
+
|
41
|
+
if value.is_a? Rack::Test::UploadedFile
|
42
|
+
@m_form.enctype = 'multipart/form-data'
|
43
|
+
|
44
|
+
ul = Mechanize::Form::FileUpload.new({'name' => key.to_s}, value.original_filename)
|
45
|
+
ul.mime_type = value.content_type
|
46
|
+
ul.file_data = (value.rewind; value.read)
|
47
|
+
|
48
|
+
@m_form.file_uploads << ul
|
49
|
+
|
50
|
+
return params
|
51
|
+
end
|
52
|
+
|
53
|
+
@m_form.fields << Mechanize::Form::Field.new({'name' => key.to_s}, value)
|
54
|
+
|
55
|
+
params
|
56
|
+
end
|
57
|
+
|
58
|
+
def use_mechanize?
|
59
|
+
driver.remote?(native['action'].to_s)
|
60
|
+
end
|
61
|
+
|
62
|
+
def form_referer
|
63
|
+
driver.last_response.page
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Capybara::Mechanize::Node < Capybara::RackTest::Node
|
2
|
+
def click
|
3
|
+
if tag_name == 'a'
|
4
|
+
super
|
5
|
+
elsif (tag_name == 'input' and %w(submit image).include?(type)) or
|
6
|
+
((tag_name == 'button') and type.nil? or type == "submit")
|
7
|
+
Capybara::Mechanize::Form.new(driver, form).submit(self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'capybara/spec/test_app'
|
2
|
+
|
3
|
+
class ExtendedTestApp < TestApp#< Sinatra::Base
|
4
|
+
set :environment, :production # so we don't get debug info that makes our test pass!
|
5
|
+
disable :protection
|
6
|
+
|
7
|
+
get %r{/redirect_to/(.*)} do
|
8
|
+
redirect params[:captures].first
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/form_with_relative_action_to_host' do
|
12
|
+
%{<form action="/request_info/host" method="post">
|
13
|
+
<input type="submit" value="submit" />
|
14
|
+
</form>}
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/request_info/form_with_no_action' do
|
18
|
+
%{<form method="post">
|
19
|
+
<input type="submit" value="submit" />
|
20
|
+
</form>}
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/relative_link_to_host' do
|
24
|
+
%{<a href="/request_info/host">host</a>}
|
25
|
+
end
|
26
|
+
|
27
|
+
get '/request_info/user_agent' do
|
28
|
+
request.user_agent
|
29
|
+
end
|
30
|
+
|
31
|
+
get '/request_info/*' do
|
32
|
+
current_request_info
|
33
|
+
end
|
34
|
+
|
35
|
+
post '/request_info/*' do
|
36
|
+
current_request_info
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/subsite/relative_link_to_host' do
|
40
|
+
%{<a href="/subsite/request_info2/host">host</a>}
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/subsite/local_link_to_host' do
|
44
|
+
%{<a href="request_info2/host">host</a>}
|
45
|
+
end
|
46
|
+
|
47
|
+
get '/subsite/request_info2/*' do
|
48
|
+
"subsite: " + current_request_info
|
49
|
+
end
|
50
|
+
|
51
|
+
get '/redirect_with_http_param' do
|
52
|
+
redirect '/redirect_target?foo=http'
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/redirect_target' do
|
56
|
+
%{correct redirect}
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def current_request_info
|
62
|
+
"Current host is #{request.url}, method #{request.request_method.downcase}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Capybara::Mechanize::Driver, 'local' do
|
4
|
+
let(:driver) { Capybara::Mechanize::Driver.new(ExtendedTestApp) }
|
5
|
+
|
6
|
+
describe ':headers option' do
|
7
|
+
it 'should always set headers' do
|
8
|
+
driver = Capybara::RackTest::Driver.new(TestApp, :headers => {'HTTP_FOO' => 'foobar'})
|
9
|
+
driver.visit('/get_header')
|
10
|
+
driver.html.should include('foobar')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should keep headers on link clicks' do
|
14
|
+
driver = Capybara::RackTest::Driver.new(TestApp, :headers => {'HTTP_FOO' => 'foobar'})
|
15
|
+
driver.visit('/header_links')
|
16
|
+
driver.find('.//a').first.click
|
17
|
+
driver.html.should include('foobar')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should keep headers on form submit' do
|
21
|
+
driver = Capybara::RackTest::Driver.new(TestApp, :headers => {'HTTP_FOO' => 'foobar'})
|
22
|
+
driver.visit('/header_links')
|
23
|
+
driver.find('.//input').first.click
|
24
|
+
driver.html.should include('foobar')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should keep headers on redirects' do
|
28
|
+
driver = Capybara::RackTest::Driver.new(TestApp, :headers => {'HTTP_FOO' => 'foobar'})
|
29
|
+
driver.visit('/get_header_via_redirect')
|
30
|
+
driver.html.should include('foobar')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ':follow_redirects option' do
|
35
|
+
it "defaults to following redirects" do
|
36
|
+
driver = Capybara::RackTest::Driver.new(TestApp)
|
37
|
+
|
38
|
+
driver.visit('/redirect')
|
39
|
+
driver.response.header['Location'].should be_nil
|
40
|
+
driver.browser.current_url.should match %r{/landed$}
|
41
|
+
end
|
42
|
+
|
43
|
+
it "is possible to not follow redirects" do
|
44
|
+
driver = Capybara::RackTest::Driver.new(TestApp, :follow_redirects => false)
|
45
|
+
|
46
|
+
driver.visit('/redirect')
|
47
|
+
driver.response.header['Location'].should match %r{/redirect_again$}
|
48
|
+
driver.browser.current_url.should match %r{/redirect$}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ':redirect_limit option' do
|
53
|
+
context "with default redirect limit" do
|
54
|
+
let(:driver) { Capybara::RackTest::Driver.new(TestApp) }
|
55
|
+
|
56
|
+
it "should follow 5 redirects" do
|
57
|
+
driver.visit("/redirect/5/times")
|
58
|
+
driver.html.should include('redirection complete')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not follow more than 6 redirects" do
|
62
|
+
expect do
|
63
|
+
driver.visit("/redirect/6/times")
|
64
|
+
end.to raise_error(Capybara::InfiniteRedirectError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with 21 redirect limit" do
|
69
|
+
let(:driver) { Capybara::RackTest::Driver.new(TestApp, :redirect_limit => 21) }
|
70
|
+
|
71
|
+
it "should follow 21 redirects" do
|
72
|
+
driver.visit("/redirect/21/times")
|
73
|
+
driver.html.should include('redirection complete')
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should not follow more than 21 redirects" do
|
77
|
+
expect do
|
78
|
+
driver.visit("/redirect/22/times")
|
79
|
+
end.to raise_error(Capybara::InfiniteRedirectError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should default to local mode for relative paths" do
|
85
|
+
driver.should_not be_remote('/')
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should default to local mode for the default host" do
|
89
|
+
driver.should_not be_remote('http://www.example.com')
|
90
|
+
end
|
91
|
+
|
92
|
+
context "with an app_host" do
|
93
|
+
|
94
|
+
before do
|
95
|
+
Capybara.app_host = 'http://www.remote.com'
|
96
|
+
end
|
97
|
+
|
98
|
+
after do
|
99
|
+
Capybara.app_host = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should treat urls as remote" do
|
103
|
+
driver.should be_remote('http://www.remote.com')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "with a default url, no app host" do
|
108
|
+
before do
|
109
|
+
Capybara.default_host = 'www.local.com'
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should allow local hosts to be set" do
|
113
|
+
Capybara::Mechanize.local_hosts = ['subdomain.local.com']
|
114
|
+
driver.should_not be_remote('http://subdomain.local.com')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should treat urls with the same host names as local" do
|
118
|
+
driver.should_not be_remote('http://www.local.com')
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should treat other urls as remote" do
|
122
|
+
driver.should be_remote('http://www.remote.com')
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should treat relative paths as remote if the previous request was remote" do
|
126
|
+
driver.visit(REMOTE_TEST_URL)
|
127
|
+
driver.should be_remote('/some_relative_link')
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should treat relative paths as local if the previous request was local" do
|
131
|
+
driver.visit('http://www.local.com')
|
132
|
+
driver.should_not be_remote('/some_relative_link')
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should receive the right host" do
|
136
|
+
driver.visit('http://www.local.com/host')
|
137
|
+
should_be_a_local_get
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should consider relative paths to be local when the previous request was local" do
|
141
|
+
driver.visit('http://www.local.com/host')
|
142
|
+
driver.visit('/host')
|
143
|
+
|
144
|
+
should_be_a_local_get
|
145
|
+
driver.should_not be_remote('/first_local')
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should consider relative paths to be remote when the previous request was remote" do
|
149
|
+
driver.visit("#{REMOTE_TEST_URL}/host")
|
150
|
+
driver.get('/host')
|
151
|
+
|
152
|
+
should_be_a_remote_get
|
153
|
+
driver.should be_remote('/second_remote')
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should always switch to the right context" do
|
157
|
+
driver.visit('http://www.local.com/host')
|
158
|
+
driver.get('/host')
|
159
|
+
driver.get("#{REMOTE_TEST_URL}/host")
|
160
|
+
driver.get('/host')
|
161
|
+
driver.get('http://www.local.com/host')
|
162
|
+
|
163
|
+
should_be_a_local_get
|
164
|
+
driver.should_not be_remote('/second_local')
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should follow redirects from local to remote" do
|
168
|
+
driver.visit("http://www.local.com/redirect_to/#{REMOTE_TEST_URL}/host")
|
169
|
+
should_be_a_remote_get
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should follow redirects from remote to local" do
|
173
|
+
driver.visit("#{REMOTE_TEST_URL}/redirect_to/http://www.local.com/host")
|
174
|
+
should_be_a_local_get
|
175
|
+
end
|
176
|
+
|
177
|
+
after do
|
178
|
+
Capybara.default_host = nil
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should raise a useful error for sites that return a 404, because it is probably a misconfiguration" do
|
182
|
+
expect {
|
183
|
+
driver.visit("http://iamreallysurethatthisdoesntexist.com/canttouchthis")
|
184
|
+
}.to raise_error(%r{Received the following error for a GET request to http://iamreallysurethatthisdoesntexist.com/canttouchthis:})
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should include the right host when remote" do
|
189
|
+
driver.visit("#{REMOTE_TEST_URL}/host")
|
190
|
+
should_be_a_remote_get
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#reset!' do
|
194
|
+
before do
|
195
|
+
Capybara.default_host = 'http://www.local.com'
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'should reset remote host' do
|
199
|
+
driver.visit("#{REMOTE_TEST_URL}/host")
|
200
|
+
should_be_a_remote_get
|
201
|
+
driver.reset!
|
202
|
+
driver.visit("/host")
|
203
|
+
should_be_a_local_get
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def should_be_a_remote_get
|
208
|
+
driver.current_url.should include(REMOTE_TEST_URL)
|
209
|
+
end
|
210
|
+
|
211
|
+
def should_be_a_local_get
|
212
|
+
driver.current_url.should include("www.local.com")
|
213
|
+
end
|
214
|
+
#
|
215
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Capybara::Mechanize::Driver, 'remote' do
|
4
|
+
before do
|
5
|
+
Capybara.app_host = REMOTE_TEST_URL
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
Capybara.app_host = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:driver) { Capybara::Mechanize::Driver.new(ExtendedTestApp) }
|
13
|
+
|
14
|
+
context "in remote mode" do
|
15
|
+
it "should pass arguments through to a get request" do
|
16
|
+
driver.visit("#{REMOTE_TEST_URL}/form/get", {:form => "success"})
|
17
|
+
driver.html.should include('success')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should pass arguments through to a post request" do
|
21
|
+
driver.post("#{REMOTE_TEST_URL}/form", {:form => "success"})
|
22
|
+
driver.html.should include('success')
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "redirect" do
|
26
|
+
it "should handle redirects with http-params" do
|
27
|
+
driver.visit "#{REMOTE_TEST_URL}/redirect_with_http_param"
|
28
|
+
driver.html.should include('correct redirect')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "for a post request" do
|
33
|
+
it 'transforms nested map in post data' do
|
34
|
+
driver.post("#{REMOTE_TEST_URL}/form", {:form => {:key => 'value'}})
|
35
|
+
driver.html.should include(':key=>"value"')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'process remote request' do
|
40
|
+
it 'transforms nested map in post data' do
|
41
|
+
driver.submit(:post, "#{REMOTE_TEST_URL}/form", {:form => {:key => 'value'}})
|
42
|
+
driver.html.should include(':key=>"value"')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TestSessions
|
4
|
+
Mechanize = Capybara::Session.new(:mechanize, TestApp)
|
5
|
+
end
|
6
|
+
|
7
|
+
Capybara::SpecHelper.run_specs TestSessions::Mechanize, "Mechanize", :skip => [
|
8
|
+
:js,
|
9
|
+
:screenshot,
|
10
|
+
:frames,
|
11
|
+
:windows,
|
12
|
+
:server
|
13
|
+
]
|
14
|
+
|
15
|
+
describe Capybara::Session do
|
16
|
+
context 'with mechanize driver' do
|
17
|
+
let(:session) { Capybara::Session.new(:mechanize, ExtendedTestApp) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
Capybara.default_host = 'http://www.local.com'
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#driver' do
|
24
|
+
it "should be a mechanize driver" do
|
25
|
+
session.driver.should be_an_instance_of(Capybara::Mechanize::Driver)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#mode' do
|
30
|
+
it "should remember the mode" do
|
31
|
+
session.mode.should == :mechanize
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#click_link' do
|
36
|
+
it "should use data-method if option is true" do
|
37
|
+
session.driver.options[:respect_data_method] = true
|
38
|
+
session.visit "/with_html"
|
39
|
+
session.click_link "A link with data-method"
|
40
|
+
session.html.should include('The requested object was deleted')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not use data-method if option is false" do
|
44
|
+
session.driver.options[:respect_data_method] = false
|
45
|
+
session.visit "/with_html"
|
46
|
+
session.click_link "A link with data-method"
|
47
|
+
session.html.should include('Not deleted')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should use data-method if available even if it's capitalized" do
|
51
|
+
session.driver.options[:respect_data_method] = true
|
52
|
+
session.visit "/with_html"
|
53
|
+
session.click_link "A link with capitalized data-method"
|
54
|
+
session.html.should include('The requested object was deleted')
|
55
|
+
end
|
56
|
+
|
57
|
+
after do
|
58
|
+
session.driver.options[:respect_data_method] = false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#attach_file" do
|
63
|
+
context "with multipart form" do
|
64
|
+
it "should submit an empty form-data section if no file is submitted" do
|
65
|
+
session.visit("/form")
|
66
|
+
session.click_button("Upload Empty")
|
67
|
+
session.html.should include('Successfully ignored empty file field.')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should use the last remote url when following relative links" do
|
73
|
+
session.visit("#{REMOTE_TEST_URL}/relative_link_to_host")
|
74
|
+
session.click_link "host"
|
75
|
+
session.body.should include("Current host is #{REMOTE_TEST_URL}/request_info/host, method get")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should use the last remote url when submitting a form with a relative action" do
|
79
|
+
session.visit("#{REMOTE_TEST_URL}/form_with_relative_action_to_host")
|
80
|
+
session.click_button "submit"
|
81
|
+
session.body.should include("Current host is #{REMOTE_TEST_URL}/request_info/host, method post")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should use the last url when submitting a form with no action" do
|
85
|
+
session.visit("#{REMOTE_TEST_URL}/request_info/form_with_no_action")
|
86
|
+
session.click_button "submit"
|
87
|
+
session.body.should include("Current host is #{REMOTE_TEST_URL}/request_info/form_with_no_action, method post")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should send correct user agent" do
|
91
|
+
session.visit("#{REMOTE_TEST_URL}/request_info/user_agent")
|
92
|
+
session.body.should include("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.853.0 Safari/535.2")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TestSessions
|
4
|
+
Mechanize = Capybara::Session.new(:mechanize, TestApp)
|
5
|
+
end
|
6
|
+
|
7
|
+
shared_context "remote tests" do
|
8
|
+
before do
|
9
|
+
Capybara.app_host = REMOTE_TEST_URL
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
Capybara.app_host = nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
session_describe = Capybara::SpecHelper.run_specs TestSessions::Mechanize, "Mechanize", :skip => [
|
18
|
+
:js,
|
19
|
+
:screenshot,
|
20
|
+
:frames,
|
21
|
+
:windows,
|
22
|
+
:server
|
23
|
+
]
|
24
|
+
|
25
|
+
session_describe.include_context("remote tests")
|
26
|
+
|
27
|
+
disabler = DisableExternalTests.new
|
28
|
+
disabler.tests_to_disable = [
|
29
|
+
['#visit', 'when Capybara.always_include_port is true', 'should fetch a response from the driver with an absolute url without a port'],
|
30
|
+
['#reset_session!', 'raises any errors caught inside the server']
|
31
|
+
]
|
32
|
+
disabler.disable(session_describe)
|
33
|
+
|
34
|
+
describe Capybara::Session do
|
35
|
+
context 'with remote mechanize driver' do
|
36
|
+
include_context 'remote tests'
|
37
|
+
|
38
|
+
let(:session) { Capybara::Session.new(:mechanize, ExtendedTestApp) }
|
39
|
+
|
40
|
+
describe '#driver' do
|
41
|
+
it "should be a mechanize driver" do
|
42
|
+
session.driver.should be_an_instance_of(Capybara::Mechanize::Driver)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#mode' do
|
47
|
+
it "should remember the mode" do
|
48
|
+
session.mode.should == :mechanize
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#click_link' do
|
53
|
+
it "should use data-method if option is true" do
|
54
|
+
session.driver.options[:respect_data_method] = true
|
55
|
+
session.visit "/with_html"
|
56
|
+
session.click_link "A link with data-method"
|
57
|
+
session.html.should include('The requested object was deleted')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not use data-method if option is false" do
|
61
|
+
session.driver.options[:respect_data_method] = false
|
62
|
+
session.visit "/with_html"
|
63
|
+
session.click_link "A link with data-method"
|
64
|
+
session.html.should include('Not deleted')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should use data-method if available even if it's capitalized" do
|
68
|
+
session.driver.options[:respect_data_method] = true
|
69
|
+
session.visit "/with_html"
|
70
|
+
session.click_link "A link with capitalized data-method"
|
71
|
+
session.html.should include('The requested object was deleted')
|
72
|
+
end
|
73
|
+
|
74
|
+
after do
|
75
|
+
session.driver.options[:respect_data_method] = false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#attach_file" do
|
80
|
+
context "with multipart form" do
|
81
|
+
it "should submit an empty form-data section if no file is submitted" do
|
82
|
+
session.visit("/form")
|
83
|
+
session.click_button("Upload Empty")
|
84
|
+
session.html.should include('Successfully ignored empty file field.')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "remote app in a sub-path" do
|
90
|
+
it "follows relative link correctly" do
|
91
|
+
session.visit "/subsite/relative_link_to_host"
|
92
|
+
session.click_link "host"
|
93
|
+
session.body.should include('request_info2/host')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "follows local link correctly" do
|
97
|
+
session.visit "/subsite/local_link_to_host"
|
98
|
+
session.click_link "host"
|
99
|
+
session.body.should include('request_info2/host')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'capybara/spec/spec_helper'
|
2
|
+
require 'capybara/mechanize'
|
3
|
+
require 'capybara/spec/extended_test_app'
|
4
|
+
|
5
|
+
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
|
6
|
+
|
7
|
+
$LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
|
8
|
+
|
9
|
+
Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
# Spruce up the focus!
|
13
|
+
config.filter_run :focus => true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
16
|
+
|
17
|
+
# Used with DisableExternalTests
|
18
|
+
config.filter_run_excluding :external_test_disabled
|
19
|
+
|
20
|
+
config.after do
|
21
|
+
Capybara::Mechanize.local_hosts = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
Capybara::SpecHelper.configure(config)
|
25
|
+
end
|
26
|
+
|
27
|
+
setup = ExtendedTestAppSetup.new.boot
|
28
|
+
REMOTE_TEST_URL = setup.remote_test_url
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class DisableExternalTests
|
2
|
+
attr_accessor :tests_to_disable
|
3
|
+
|
4
|
+
def disable(top_level_example_group)
|
5
|
+
tests_to_disable.each do |to_disable|
|
6
|
+
example_group = top_level_example_group
|
7
|
+
|
8
|
+
example_description = to_disable.pop
|
9
|
+
|
10
|
+
to_disable.each do |description|
|
11
|
+
example_group = example_group.children.select{ |g| g.description == description }.first
|
12
|
+
end
|
13
|
+
|
14
|
+
example = example_group.examples.select{ |e| e.description == example_description }.first
|
15
|
+
|
16
|
+
example.metadata[:external_test_disabled] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# This class works around some weirdness with Capybara's test suite and sinatra's behavior.
|
2
|
+
# We need to make sure that sinatra uses TestApp for at least one request before the Capybara session
|
3
|
+
# specs run. Without this we get errors from sinatra trying to handle requests with TestApp.clone
|
4
|
+
class ExtendedTestAppSetup
|
5
|
+
include Capybara::DSL
|
6
|
+
|
7
|
+
attr_reader :remote_test_url
|
8
|
+
|
9
|
+
def boot
|
10
|
+
boot_test_app
|
11
|
+
boot_remote_app
|
12
|
+
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def boot_test_app
|
17
|
+
Capybara.app = TestApp
|
18
|
+
dummy_server = Capybara::Server.new(TestApp)
|
19
|
+
dummy_server.boot
|
20
|
+
|
21
|
+
# Boot TestApp's Sinatra
|
22
|
+
visit '/'
|
23
|
+
end
|
24
|
+
|
25
|
+
def boot_remote_app
|
26
|
+
remote_server = Capybara::Server.new(ExtendedTestApp)
|
27
|
+
remote_server.boot
|
28
|
+
@remote_test_url = "http://localhost:#{remote_server.port}"
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ryansch-capybara-mechanize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeroen van Dijk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mechanize
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.5'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.5'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: capybara
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.0'
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.0.1
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.0'
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 2.0.1
|
52
|
+
description: RackTest driver for Capybara, but with remote request support thanks
|
53
|
+
to mechanize
|
54
|
+
email: jeroen@jeevidee.nl
|
55
|
+
executables: []
|
56
|
+
extensions: []
|
57
|
+
extra_rdoc_files: []
|
58
|
+
files:
|
59
|
+
- lib/capybara/mechanize/browser.rb
|
60
|
+
- lib/capybara/mechanize/cucumber.rb
|
61
|
+
- lib/capybara/mechanize/driver.rb
|
62
|
+
- lib/capybara/mechanize/form.rb
|
63
|
+
- lib/capybara/mechanize/node.rb
|
64
|
+
- lib/capybara/mechanize/version.rb
|
65
|
+
- lib/capybara/mechanize.rb
|
66
|
+
- lib/capybara/spec/extended_test_app.rb
|
67
|
+
- spec/driver/mechanize_driver_spec.rb
|
68
|
+
- spec/driver/remote_mechanize_driver_spec.rb
|
69
|
+
- spec/session/mechanize_spec.rb
|
70
|
+
- spec/session/remote_mechanize_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/support/disable_external_tests.rb
|
73
|
+
- spec/support/extended_test_app_setup.rb
|
74
|
+
- README.mdown
|
75
|
+
homepage: https://github.com/ryansch/capybara-mechanize
|
76
|
+
licenses: []
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --charset=UTF-8
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
hash: 728688646484683167
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.8.24
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: RackTest driver for Capybara with remote request support
|
103
|
+
test_files: []
|