mechwarrior 0.0.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.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
+