nimboids-capybara 1.1.2

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.
Files changed (105) hide show
  1. data/History.txt +289 -0
  2. data/README.rdoc +722 -0
  3. data/lib/capybara.rb +252 -0
  4. data/lib/capybara/cucumber.rb +28 -0
  5. data/lib/capybara/driver/base.rb +64 -0
  6. data/lib/capybara/driver/node.rb +74 -0
  7. data/lib/capybara/dsl.rb +168 -0
  8. data/lib/capybara/node/actions.rb +162 -0
  9. data/lib/capybara/node/base.rb +63 -0
  10. data/lib/capybara/node/document.rb +25 -0
  11. data/lib/capybara/node/element.rb +201 -0
  12. data/lib/capybara/node/finders.rb +197 -0
  13. data/lib/capybara/node/matchers.rb +417 -0
  14. data/lib/capybara/node/simple.rb +132 -0
  15. data/lib/capybara/rack_test/browser.rb +121 -0
  16. data/lib/capybara/rack_test/driver.rb +80 -0
  17. data/lib/capybara/rack_test/form.rb +80 -0
  18. data/lib/capybara/rack_test/node.rb +105 -0
  19. data/lib/capybara/rails.rb +17 -0
  20. data/lib/capybara/rspec.rb +26 -0
  21. data/lib/capybara/rspec/features.rb +22 -0
  22. data/lib/capybara/rspec/matchers.rb +154 -0
  23. data/lib/capybara/selector.rb +89 -0
  24. data/lib/capybara/selenium/driver.rb +163 -0
  25. data/lib/capybara/selenium/node.rb +91 -0
  26. data/lib/capybara/server.rb +90 -0
  27. data/lib/capybara/session.rb +321 -0
  28. data/lib/capybara/spec/driver.rb +301 -0
  29. data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
  30. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  31. data/lib/capybara/spec/public/test.js +43 -0
  32. data/lib/capybara/spec/session.rb +154 -0
  33. data/lib/capybara/spec/session/all_spec.rb +78 -0
  34. data/lib/capybara/spec/session/attach_file_spec.rb +73 -0
  35. data/lib/capybara/spec/session/check_spec.rb +65 -0
  36. data/lib/capybara/spec/session/choose_spec.rb +26 -0
  37. data/lib/capybara/spec/session/click_button_spec.rb +304 -0
  38. data/lib/capybara/spec/session/click_link_or_button_spec.rb +36 -0
  39. data/lib/capybara/spec/session/click_link_spec.rb +119 -0
  40. data/lib/capybara/spec/session/current_host_spec.rb +68 -0
  41. data/lib/capybara/spec/session/current_url_spec.rb +15 -0
  42. data/lib/capybara/spec/session/fill_in_spec.rb +125 -0
  43. data/lib/capybara/spec/session/find_button_spec.rb +18 -0
  44. data/lib/capybara/spec/session/find_by_id_spec.rb +18 -0
  45. data/lib/capybara/spec/session/find_field_spec.rb +26 -0
  46. data/lib/capybara/spec/session/find_link_spec.rb +19 -0
  47. data/lib/capybara/spec/session/find_spec.rb +149 -0
  48. data/lib/capybara/spec/session/first_spec.rb +105 -0
  49. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  50. data/lib/capybara/spec/session/has_content_spec.rb +106 -0
  51. data/lib/capybara/spec/session/has_css_spec.rb +243 -0
  52. data/lib/capybara/spec/session/has_field_spec.rb +192 -0
  53. data/lib/capybara/spec/session/has_link_spec.rb +37 -0
  54. data/lib/capybara/spec/session/has_select_spec.rb +129 -0
  55. data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
  56. data/lib/capybara/spec/session/has_table_spec.rb +96 -0
  57. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  58. data/lib/capybara/spec/session/headers.rb +19 -0
  59. data/lib/capybara/spec/session/javascript.rb +289 -0
  60. data/lib/capybara/spec/session/response_code.rb +19 -0
  61. data/lib/capybara/spec/session/select_spec.rb +113 -0
  62. data/lib/capybara/spec/session/text_spec.rb +19 -0
  63. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  64. data/lib/capybara/spec/session/unselect_spec.rb +61 -0
  65. data/lib/capybara/spec/session/within_spec.rb +178 -0
  66. data/lib/capybara/spec/test_app.rb +142 -0
  67. data/lib/capybara/spec/views/buttons.erb +4 -0
  68. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  69. data/lib/capybara/spec/views/form.erb +365 -0
  70. data/lib/capybara/spec/views/frame_one.erb +8 -0
  71. data/lib/capybara/spec/views/frame_two.erb +8 -0
  72. data/lib/capybara/spec/views/header_links.erb +7 -0
  73. data/lib/capybara/spec/views/host_links.erb +12 -0
  74. data/lib/capybara/spec/views/popup_one.erb +8 -0
  75. data/lib/capybara/spec/views/popup_two.erb +8 -0
  76. data/lib/capybara/spec/views/postback.erb +13 -0
  77. data/lib/capybara/spec/views/tables.erb +122 -0
  78. data/lib/capybara/spec/views/with_html.erb +78 -0
  79. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  80. data/lib/capybara/spec/views/with_js.erb +48 -0
  81. data/lib/capybara/spec/views/with_scope.erb +36 -0
  82. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  83. data/lib/capybara/spec/views/within_frames.erb +10 -0
  84. data/lib/capybara/spec/views/within_popups.erb +25 -0
  85. data/lib/capybara/util/save_and_open_page.rb +44 -0
  86. data/lib/capybara/util/timeout.rb +27 -0
  87. data/lib/capybara/version.rb +3 -0
  88. data/spec/basic_node_spec.rb +77 -0
  89. data/spec/capybara_spec.rb +46 -0
  90. data/spec/driver/rack_test_driver_spec.rb +89 -0
  91. data/spec/driver/selenium_driver_spec.rb +50 -0
  92. data/spec/dsl_spec.rb +253 -0
  93. data/spec/fixtures/selenium_driver_rspec_failure.rb +8 -0
  94. data/spec/fixtures/selenium_driver_rspec_success.rb +8 -0
  95. data/spec/rspec/features_spec.rb +45 -0
  96. data/spec/rspec/matchers_spec.rb +495 -0
  97. data/spec/rspec_spec.rb +53 -0
  98. data/spec/save_and_open_page_spec.rb +155 -0
  99. data/spec/server_spec.rb +89 -0
  100. data/spec/session/rack_test_session_spec.rb +55 -0
  101. data/spec/session/selenium_session_spec.rb +26 -0
  102. data/spec/spec_helper.rb +30 -0
  103. data/spec/string_spec.rb +77 -0
  104. data/spec/timeout_spec.rb +28 -0
  105. metadata +346 -0
@@ -0,0 +1,132 @@
1
+ module Capybara
2
+ module Node
3
+
4
+ ##
5
+ #
6
+ # A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
7
+ # includes only {Capybara::Node::Finders} and {Capybara::Node::Matchers} and does
8
+ # not include {Capybara::Node::Actions}. This type of node is returned when
9
+ # using {Capybara.string}.
10
+ #
11
+ # It is useful in that it does not require a session, an application or a driver,
12
+ # but can still use Capybara's finders and matchers on any string that contains HTML.
13
+ #
14
+ class Simple
15
+ include Capybara::Node::Finders
16
+ include Capybara::Node::Matchers
17
+
18
+ attr_reader :native
19
+
20
+ def initialize(native)
21
+ native = Nokogiri::HTML(native) if native.is_a?(String)
22
+ @native = native
23
+ end
24
+
25
+ ##
26
+ #
27
+ # @return [String] The text of the element
28
+ #
29
+ def text
30
+ native.text
31
+ end
32
+
33
+ ##
34
+ #
35
+ # Retrieve the given attribute
36
+ #
37
+ # element[:title] # => HTML title attribute
38
+ #
39
+ # @param [Symbol] attribute The attribute to retrieve
40
+ # @return [String] The value of the attribute
41
+ #
42
+ def [](name)
43
+ attr_name = name.to_s
44
+ if attr_name == 'value'
45
+ value
46
+ elsif 'input' == tag_name and 'checkbox' == native[:type] and 'checked' == attr_name
47
+ native['checked'] == 'checked'
48
+ else
49
+ native[attr_name]
50
+ end
51
+ end
52
+
53
+ ##
54
+ #
55
+ # @return [String] The tag name of the element
56
+ #
57
+ def tag_name
58
+ native.node_name
59
+ end
60
+
61
+ ##
62
+ #
63
+ # An XPath expression describing where on the page the element can be found
64
+ #
65
+ # @return [String] An XPath expression
66
+ #
67
+ def path
68
+ native.path
69
+ end
70
+
71
+ ##
72
+ #
73
+ # @return [String] The value of the form element
74
+ #
75
+ def value
76
+ if tag_name == 'textarea'
77
+ native.content.sub(/\A\n/, '')
78
+ elsif tag_name == 'select'
79
+ if native['multiple'] == 'multiple'
80
+ native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
81
+ else
82
+ option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
83
+ option[:value] || option.content if option
84
+ end
85
+ else
86
+ native[:value]
87
+ end
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Whether or not the element is visible. Does not support CSS, so
93
+ # the result may be inaccurate.
94
+ #
95
+ # @return [Boolean] Whether the element is visible
96
+ #
97
+ def visible?
98
+ native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
99
+ end
100
+
101
+ ##
102
+ #
103
+ # Whether or not the element is checked.
104
+ #
105
+ # @return [Boolean] Whether the element is checked
106
+ #
107
+ def checked?
108
+ native[:checked]
109
+ end
110
+
111
+ ##
112
+ #
113
+ # Whether or not the element is selected.
114
+ #
115
+ # @return [Boolean] Whether the element is selected
116
+ #
117
+ def selected?
118
+ native[:selected]
119
+ end
120
+
121
+ protected
122
+
123
+ def find_in_base(selector, xpath)
124
+ native.xpath(xpath).map { |node| self.class.new(node) }
125
+ end
126
+
127
+ def wait_until
128
+ yield # simple nodes don't need to wait
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,121 @@
1
+ class Capybara::RackTest::Browser
2
+ include ::Rack::Test::Methods
3
+
4
+ attr_reader :driver
5
+ attr_accessor :current_host
6
+
7
+ def initialize(driver)
8
+ @driver = driver
9
+ end
10
+
11
+ def app
12
+ driver.app
13
+ end
14
+
15
+ def options
16
+ driver.options
17
+ end
18
+
19
+ def visit(path, attributes = {})
20
+ reset_host!
21
+ process(:get, path, attributes)
22
+ follow_redirects!
23
+ end
24
+
25
+ def submit(method, path, attributes)
26
+ path = request_path if not path or path.empty?
27
+ process(method, path, attributes)
28
+ follow_redirects!
29
+ end
30
+
31
+ def follow(method, path, attributes = {})
32
+ return if path.gsub(/^#{request_path}/, '').start_with?('#')
33
+ process(method, path, attributes)
34
+ follow_redirects!
35
+ end
36
+
37
+ def follow_redirects!
38
+ 5.times do
39
+ process(:get, last_response["Location"]) if last_response.redirect?
40
+ end
41
+ raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if last_response.redirect?
42
+ end
43
+
44
+ def process(method, path, attributes = {})
45
+ new_uri = URI.parse(path)
46
+ current_uri = URI.parse(current_url)
47
+
48
+ if new_uri.host
49
+ @current_host = new_uri.scheme + '://' + new_uri.host
50
+ end
51
+
52
+ if new_uri.relative?
53
+ if path.start_with?('?')
54
+ path = request_path + path
55
+ elsif not path.start_with?('/')
56
+ path = request_path.sub(%r(/[^/]*$), '/') + path
57
+ end
58
+ path = current_host + path
59
+ end
60
+
61
+ reset_cache!
62
+ send(method, path, attributes, env)
63
+ end
64
+
65
+ def current_url
66
+ last_request.url
67
+ rescue Rack::Test::Error
68
+ ""
69
+ end
70
+
71
+ def reset_host!
72
+ @current_host = (Capybara.app_host || Capybara.default_host)
73
+ end
74
+
75
+ def reset_cache!
76
+ @dom = nil
77
+ end
78
+
79
+ def body
80
+ dom.to_xml
81
+ end
82
+
83
+ def dom
84
+ @dom ||= Nokogiri::HTML(source)
85
+ end
86
+
87
+ def find(selector)
88
+ dom.xpath(selector).map { |node| Capybara::RackTest::Node.new(self, node) }
89
+ end
90
+
91
+ def source
92
+ last_response.body
93
+ rescue Rack::Test::Error
94
+ nil
95
+ end
96
+
97
+ protected
98
+
99
+ def build_rack_mock_session
100
+ reset_host! unless current_host
101
+ Rack::MockSession.new(app, URI.parse(current_host).host)
102
+ end
103
+
104
+ def request_path
105
+ last_request.path
106
+ rescue Rack::Test::Error
107
+ ""
108
+ end
109
+
110
+ def env
111
+ env = {}
112
+ begin
113
+ env["HTTP_REFERER"] = last_request.url
114
+ rescue Rack::Test::Error
115
+ # no request yet
116
+ end
117
+ env.merge!(options[:headers]) if options[:headers]
118
+ env
119
+ end
120
+
121
+ end
@@ -0,0 +1,80 @@
1
+ require 'rack/test'
2
+ require 'rack/utils'
3
+ require 'mime/types'
4
+ require 'nokogiri'
5
+ require 'cgi'
6
+
7
+ class Capybara::RackTest::Driver < Capybara::Driver::Base
8
+ DEFAULT_OPTIONS = {
9
+ :respect_data_method => true
10
+ }
11
+ attr_reader :app, :options
12
+
13
+ def initialize(app, options={})
14
+ raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
15
+ @app = app
16
+ @options = DEFAULT_OPTIONS.merge(options)
17
+ end
18
+
19
+ def browser
20
+ @browser ||= Capybara::RackTest::Browser.new(self)
21
+ end
22
+
23
+ def response
24
+ browser.last_response
25
+ end
26
+
27
+ def request
28
+ browser.last_request
29
+ end
30
+
31
+ def visit(path, attributes = {})
32
+ browser.visit(path, attributes)
33
+ end
34
+
35
+ def submit(method, path, attributes)
36
+ browser.submit(method, path, attributes)
37
+ end
38
+
39
+ def follow(method, path, attributes = {})
40
+ browser.follow(method, path, attributes)
41
+ end
42
+
43
+ def current_url
44
+ browser.current_url
45
+ end
46
+
47
+ def response_headers
48
+ response.headers
49
+ end
50
+
51
+ def status_code
52
+ response.status
53
+ end
54
+
55
+ def find(selector)
56
+ browser.find(selector)
57
+ end
58
+
59
+ def body
60
+ browser.body
61
+ end
62
+
63
+ def source
64
+ browser.source
65
+ end
66
+
67
+ def dom
68
+ browser.dom
69
+ end
70
+
71
+ def reset!
72
+ @browser = nil
73
+ end
74
+
75
+ def get(*args, &block); browser.get(*args, &block); end
76
+ def post(*args, &block); browser.post(*args, &block); end
77
+ def put(*args, &block); browser.put(*args, &block); end
78
+ def delete(*args, &block); browser.delete(*args, &block); end
79
+ def header(key, value); browser.header(key, value); end
80
+ end
@@ -0,0 +1,80 @@
1
+ class Capybara::RackTest::Form < Capybara::RackTest::Node
2
+ # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
3
+ # the class specifically when determing whether to consturct the request as multipart.
4
+ # That check should be based solely on the form element's 'enctype' attribute value,
5
+ # which should probably be provided to Rack::Test in its non-GET request methods.
6
+ class NilUploadedFile < Rack::Test::UploadedFile
7
+ def initialize
8
+ @empty_file = Tempfile.new("nil_uploaded_file")
9
+ @empty_file.close
10
+ end
11
+
12
+ def original_filename; ""; end
13
+ def content_type; "application/octet-stream"; end
14
+ def path; @empty_file.path; end
15
+ end
16
+
17
+ def params(button)
18
+ params = {}
19
+
20
+ native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
21
+ case field.name
22
+ when 'input'
23
+ if %w(radio checkbox).include? field['type']
24
+ merge_param!(params, field['name'].to_s, field['value'].to_s) if field['checked']
25
+ elsif %w(submit image).include? field['type']
26
+ # TO DO identify the click button here (in document order, rather
27
+ # than leaving until the end of the params)
28
+ elsif field['type'] =='file'
29
+ if multipart?
30
+ file = \
31
+ if (value = field['value']).to_s.empty?
32
+ NilUploadedFile.new
33
+ else
34
+ content_type = MIME::Types.type_for(value).first.to_s
35
+ Rack::Test::UploadedFile.new(value, content_type)
36
+ end
37
+ merge_param!(params, field['name'].to_s, file)
38
+ else
39
+ merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
40
+ end
41
+ else
42
+ merge_param!(params, field['name'].to_s, field['value'].to_s)
43
+ end
44
+ when 'select'
45
+ if field['multiple'] == 'multiple'
46
+ options = field.xpath(".//option[@selected]")
47
+ options.each do |option|
48
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
49
+ end
50
+ else
51
+ option = field.xpath(".//option[@selected]").first
52
+ option ||= field.xpath('.//option').first
53
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
54
+ end
55
+ when 'textarea'
56
+ merge_param!(params, field['name'].to_s, field.text.to_s)
57
+ end
58
+ end
59
+ merge_param!(params, button[:name], button[:value] || "") if button[:name]
60
+ params
61
+ end
62
+
63
+ def submit(button)
64
+ driver.submit(method, native['action'].to_s, params(button))
65
+ end
66
+
67
+ def multipart?
68
+ self[:enctype] == "multipart/form-data"
69
+ end
70
+
71
+ private
72
+
73
+ def method
74
+ self[:method] =~ /post/i ? :post : :get
75
+ end
76
+
77
+ def merge_param!(params, key, value)
78
+ Rack::Utils.normalize_params(params, key, value)
79
+ end
80
+ end
@@ -0,0 +1,105 @@
1
+ class Capybara::RackTest::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
+ !self[:maxlength].empty?
28
+ # Browser behavior for maxlength="0" is inconsistent, so we stick with
29
+ # Firefox, allowing no input
30
+ value = value[0...self[:maxlength].to_i]
31
+ end
32
+ native['value'] = value.to_s
33
+ elsif tag_name == "textarea"
34
+ native.content = value.to_s
35
+ end
36
+ end
37
+
38
+ def select_option
39
+ if select_node['multiple'] != 'multiple'
40
+ select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
41
+ end
42
+ native["selected"] = 'selected'
43
+ end
44
+
45
+ def unselect_option
46
+ if select_node['multiple'] != 'multiple'
47
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
48
+ end
49
+ native.remove_attribute('selected')
50
+ end
51
+
52
+ def click
53
+ if tag_name == 'a'
54
+ method = self["data-method"] if driver.options[:respect_data_method]
55
+ method ||= :get
56
+ driver.follow(method, self[:href].to_s)
57
+ elsif (tag_name == 'input' and %w(submit image).include?(type)) or
58
+ ((tag_name == 'button') and type.nil? or type == "submit")
59
+ Capybara::RackTest::Form.new(driver, form).submit(self)
60
+ end
61
+ end
62
+
63
+ def tag_name
64
+ native.node_name
65
+ end
66
+
67
+ def visible?
68
+ string_node.visible?
69
+ end
70
+
71
+ def checked?
72
+ string_node.checked?
73
+ end
74
+
75
+ def selected?
76
+ string_node.selected?
77
+ end
78
+
79
+ def path
80
+ native.path
81
+ end
82
+
83
+ def find(locator)
84
+ native.xpath(locator).map { |n| self.class.new(driver, n) }
85
+ end
86
+
87
+ private
88
+
89
+ def string_node
90
+ @string_node ||= Capybara::Node::Simple.new(native)
91
+ end
92
+
93
+ # a reference to the select node if this is an option node
94
+ def select_node
95
+ find('./ancestor::select').first
96
+ end
97
+
98
+ def type
99
+ native[:type]
100
+ end
101
+
102
+ def form
103
+ native.ancestors('form').first
104
+ end
105
+ end