ryansch-capybara-mechanize 1.0.0
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.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: []
|