mechwarrior 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ Mechwarrior
2
+ ===========
3
+
4
+ A Mechanize based Capybara driver, designed to get round the rack-test limitation of not being able to visit external (i.e. not the app your testing) websites.
5
+
6
+ Installation
7
+ ------------
8
+
9
+ 1. `gem install 'mechwarrior'`
10
+
11
+ 2. `require 'mechwarrior/cucumber'` in cucumbers env.rb
12
+
13
+ 3. Tag your steps with either @mechanize or @external
14
+
15
+ Dependencies
16
+ ------------
17
+ capybara ">= 1.0.0"
18
+ mechanize "~> 2.0.0"
19
+
20
+
21
+ Licence
22
+ ---------
23
+
24
+ Copyright (c) 2011, Chris Lowder, http://github.com/clowder
25
+
26
+ All rights reserved.
27
+
28
+ Redistribution and use in source and binary forms, with or without
29
+ modification, are permitted provided that the following conditions are met:
30
+
31
+ * Redistributions of source code must retain the above copyright notice, this
32
+ list of conditions and the following disclaimer.
33
+
34
+ * Redistributions in binary form must reproduce the above copyright notice,
35
+ this list of conditions and the following disclaimer in the documentation
36
+ and/or other materials provided with the distribution.
37
+
38
+ * Neither the name of http://github.com/clowder nor the names of its contributors may
39
+ be used to endorse or promote products derived from this software without
40
+ specific prior written permission.
41
+
42
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
46
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
47
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
48
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
50
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
51
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
52
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,118 @@
1
+ require 'mechanize'
2
+
3
+ class Capybara::Mechanize::Driver < Capybara::Driver::Base
4
+ attr_reader :app, :rack_server, :options
5
+
6
+ def initialize(app, options={})
7
+ raise ArgumentError, "Mechanize requires a rack application, but none was given" unless app
8
+
9
+ @app = app
10
+ @options = options
11
+ @rack_server = Capybara::Server.new(@app)
12
+ @rack_server.boot if Capybara.run_server
13
+ end
14
+
15
+ def browser
16
+ unless @browser
17
+ @browser = Mechanize.new
18
+ @browser.follow_meta_refresh
19
+ end
20
+ @browser
21
+ end
22
+
23
+ def visit(path)
24
+ browser.get(url(path))
25
+ end
26
+
27
+ def follow(_method, path, attributes={})
28
+ return if is_javascript_hashy_path?(path)
29
+
30
+ absolute_url = make_absolute_url(path)
31
+
32
+ if _method == :get
33
+ browser.get(absolute_url, [], self.current_url)
34
+ else
35
+ browser.send(_method, absolute_url, attributes)
36
+ end
37
+ end
38
+
39
+ def submit(form_node, button_node)
40
+ form = Capybara::Mechanize::Form.new(form_node, browser)
41
+ button = Mechanize::Form::Button.new(button_node)
42
+
43
+ form.action = form.action || self.current_url
44
+
45
+ browser.submit(form, button)
46
+ end
47
+
48
+ def current_url
49
+ current_page? ? current_page.uri.to_s : ''
50
+ end
51
+
52
+ def response_headers
53
+ current_page? ? current_page.header : {}
54
+ end
55
+
56
+ def status_code
57
+ current_page? ? current_page.code.to_i : nil
58
+ end
59
+
60
+ def find(selector)
61
+ dom.search(selector).map { |node| Capybara::Mechanize::Node.new(self, node) }
62
+ end
63
+
64
+ def source
65
+ current_page? ? current_page.body : ''
66
+ end
67
+ alias :body :source
68
+
69
+ def dom
70
+ current_page? ? current_page.root : Nokogiri::HTML(nil)
71
+ end
72
+
73
+ def reset!
74
+ @browser = nil
75
+ end
76
+
77
+ private
78
+
79
+ def url(path)
80
+ rack_server.url(path)
81
+ end
82
+
83
+ def current_page
84
+ browser.current_page
85
+ end
86
+
87
+ def current_page?
88
+ !browser.current_page.nil?
89
+ end
90
+
91
+ def current_page_is_external?
92
+ current_page.uri.host != (Capybara.app_host || rack_server.host)
93
+ end
94
+
95
+ def request_path
96
+ browser.current_page.uri.path
97
+ end
98
+
99
+ def make_absolute_url(path)
100
+ return path unless URI.parse(path).relative?
101
+
102
+ path = "#{ request_path }#{ path }" if is_query_string?(path)
103
+
104
+ if current_page? && current_page_is_external?
105
+ current_page.uri.merge(path).to_s
106
+ else
107
+ url(path)
108
+ end
109
+ end
110
+
111
+ def is_query_string?(path)
112
+ path.start_with?('?')
113
+ end
114
+
115
+ def is_javascript_hashy_path?(path)
116
+ path.gsub(/^#{request_path}/, '').start_with?('#')
117
+ end
118
+ end
@@ -0,0 +1,43 @@
1
+ require 'mechanize'
2
+
3
+ class Capybara::Mechanize::Form < Mechanize::Form
4
+
5
+ def initialize(node, mech=nil, page=nil)
6
+ super(strip_disabled_nodes(node), mech, page)
7
+ normalize_uploads
8
+ end
9
+
10
+ private
11
+
12
+ def strip_disabled_nodes(form_node)
13
+ stripped_form_node = form_node.clone
14
+ stripped_form_node.search('*[disabled=disabled]').remove
15
+ stripped_form_node
16
+ end
17
+
18
+ def normalize_uploads
19
+ if self.enctype.downcase =~ /^multipart\/form-data/
20
+ assign_files_to_uploads
21
+ else
22
+ create_dummy_fields
23
+ end
24
+ end
25
+
26
+ def assign_files_to_uploads
27
+ self.file_uploads.each do |upload|
28
+ file_path = upload.node["value"]
29
+ upload.file_name = file_path if !file_path.nil? && file_path != ''
30
+ end
31
+ end
32
+
33
+ def create_dummy_fields
34
+ self.file_uploads.each do |upload|
35
+ file_path = upload.node["value"]
36
+
37
+ if !file_path.nil? && file_path != ''
38
+ new_field = Mechanize::Form::Field.new(upload.node, File.basename(file_path))
39
+ self.fields << new_field
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,100 @@
1
+ class Capybara::Mechanize::Node < Capybara::Driver::Node
2
+ def text
3
+ native.text
4
+ end
5
+
6
+ def [](name)
7
+ string_node[name]
8
+ end
9
+
10
+ def value
11
+ string_node.value
12
+ end
13
+
14
+ def set(value)
15
+ if tag_name == 'input' and type == 'radio'
16
+ other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
17
+ driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
18
+ native['checked'] = 'checked'
19
+ elsif tag_name == 'input' and type == 'checkbox'
20
+ if value && !native['checked']
21
+ native['checked'] = 'checked'
22
+ elsif !value && native['checked']
23
+ native.remove_attribute('checked')
24
+ end
25
+ elsif tag_name == 'input'
26
+ if (type == 'text' || type == 'password') && self[:maxlength]
27
+ value = value[0...self[:maxlength].to_i]
28
+ end
29
+ native['value'] = value.to_s
30
+ elsif tag_name == "textarea"
31
+ native.content = value.to_s
32
+ end
33
+ end
34
+
35
+ def select_option
36
+ if select_node['multiple'] != 'multiple'
37
+ select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
38
+ end
39
+ native["selected"] = 'selected'
40
+ end
41
+
42
+ def unselect_option
43
+ if select_node['multiple'] != 'multiple'
44
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
45
+ end
46
+ native.remove_attribute('selected')
47
+ end
48
+
49
+ def click
50
+ if tag_name == 'a'
51
+ method = self["data-method"] || :get
52
+ driver.follow(method, self[:href].to_s)
53
+ elsif (tag_name == 'input' && %w(submit image).include?(type)) || ((tag_name == 'button') && type.nil? || type == "submit")
54
+ driver.submit(form, self)
55
+ end
56
+ end
57
+
58
+ def tag_name
59
+ native.node_name
60
+ end
61
+
62
+ def visible?
63
+ string_node.visible?
64
+ end
65
+
66
+ def checked?
67
+ string_node.checked?
68
+ end
69
+
70
+ def selected?
71
+ string_node.selected?
72
+ end
73
+
74
+ def path
75
+ native.path
76
+ end
77
+
78
+ def find(locator)
79
+ native.xpath(locator).map { |n| self.class.new(driver, n) }
80
+ end
81
+
82
+ private
83
+
84
+ def string_node
85
+ @string_node ||= Capybara::Node::Simple.new(native)
86
+ end
87
+
88
+ # a reference to the select node if this is an option node
89
+ def select_node
90
+ find('./ancestor::select').first
91
+ end
92
+
93
+ def type
94
+ native[:type]
95
+ end
96
+
97
+ def form
98
+ native.ancestors('form').first
99
+ end
100
+ end
@@ -0,0 +1,13 @@
1
+ require 'capybara'
2
+
3
+ module Capybara
4
+ module Mechanize
5
+ autoload :Driver, 'capybara/mechanize/driver'
6
+ autoload :Node, 'capybara/mechanize/node'
7
+ autoload :Form, 'capybara/mechanize/form'
8
+ end
9
+ end
10
+
11
+ Capybara.register_driver :mechanize do |app|
12
+ Capybara::Mechanize::Driver.new(app)
13
+ end
@@ -0,0 +1,5 @@
1
+ require 'mechwarrior'
2
+
3
+ Before '@external' do
4
+ Capybara.current_driver = :mechanize
5
+ end
@@ -0,0 +1,3 @@
1
+ module Mechwarrior
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'capybara/mechanize'
2
+
3
+ module Mechwarrior
4
+ autoload :VERSION, 'mechwarrior/version'
5
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Capybara::Mechanize::Driver do
5
+ before do
6
+ @driver = TestSessions::Mechanize.driver
7
+ end
8
+
9
+ it "should throw an error when no rack app is given" do
10
+ running do
11
+ Capybara::Mechanize::Driver.new(nil)
12
+ end.should raise_error(ArgumentError)
13
+ end
14
+
15
+ it_should_behave_like "driver"
16
+ it_should_behave_like "driver with header support"
17
+ it_should_behave_like "driver with status code support"
18
+ it_should_behave_like "driver with cookies support"
19
+
20
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Capybara::Session do
4
+ context 'with Mechanize driver' do
5
+ before do
6
+ @session = TestSessions::Mechanize
7
+ end
8
+
9
+ describe '#driver' do
10
+ it "should be a Mechanize driver" do
11
+ @session.driver.should be_an_instance_of(Capybara::Mechanize::Driver)
12
+ end
13
+ end
14
+
15
+ describe '#mode' do
16
+ it "should remember the mode" do
17
+ @session.mode.should == :mechanize
18
+ end
19
+ end
20
+
21
+ describe '#click_link' do
22
+ it "should use data-method if available" do
23
+ @session.visit "/with_html"
24
+ @session.click_link "A link with data-method"
25
+ @session.body.should include('The requested object was deleted')
26
+ end
27
+ end
28
+
29
+ it_should_behave_like "session"
30
+ it_should_behave_like "session without javascript support"
31
+ it_should_behave_like "session with headers support"
32
+ it_should_behave_like "session with status code support"
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
+
3
+ require 'rubygems'
4
+ require "bundler/setup"
5
+
6
+ require 'rspec'
7
+ require 'mechwarrior'
8
+
9
+ RSpec.configure do |config|
10
+ config.before do
11
+ Capybara.configure do |config|
12
+ config.default_selector = :xpath
13
+ end
14
+ end
15
+ end
16
+
17
+ require 'capybara/spec/driver'
18
+ require 'capybara/spec/session'
19
+
20
+ alias :running :lambda
21
+
22
+ Capybara.default_wait_time = 0 # less timeout so tests run faster
23
+
24
+ module TestSessions
25
+ Mechanize = Capybara::Session.new(:mechanize, TestApp)
26
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mechwarrior
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Chris Lowder
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-25 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: capybara
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.0.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: mechanize
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 2.0.0
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: sinatra
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.4
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.0.0
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: launchy
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.3.5
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: yard
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 0.5.8
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: fuubar
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 0.0.1
91
+ type: :development
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: cucumber
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0.10"
102
+ type: :development
103
+ version_requirements: *id008
104
+ description: A Mechanize based Capybara driver.
105
+ email:
106
+ - clowder@gmail.com
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files:
112
+ - README.md
113
+ files:
114
+ - lib/capybara/mechanize/driver.rb
115
+ - lib/capybara/mechanize/form.rb
116
+ - lib/capybara/mechanize/node.rb
117
+ - lib/capybara/mechanize.rb
118
+ - lib/mechwarrior/cucumber.rb
119
+ - lib/mechwarrior/version.rb
120
+ - lib/mechwarrior.rb
121
+ - spec/mechanize_driver_spec.rb
122
+ - spec/mechanize_session_spec.rb
123
+ - spec/spec_helper.rb
124
+ - README.md
125
+ has_rdoc: true
126
+ homepage: http://github.com/clowder/mechwarrior
127
+ licenses: []
128
+
129
+ post_install_message:
130
+ rdoc_options:
131
+ - --main
132
+ - README.md
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: "0"
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: "0"
147
+ requirements: []
148
+
149
+ rubyforge_project:
150
+ rubygems_version: 1.6.2
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: A Mechanize based Capybara driver, designed to get round the rack-test limitation of not being able to visit external (i.e. not the app your testing) websites.
154
+ test_files: []
155
+