capybara 2.0.0.beta2 → 2.0.0.beta4

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 (93) hide show
  1. data/History.txt +30 -1
  2. data/README.md +52 -31
  3. data/lib/capybara.rb +1 -0
  4. data/lib/capybara/driver/base.rb +1 -1
  5. data/lib/capybara/driver/node.rb +1 -0
  6. data/lib/capybara/dsl.rb +12 -5
  7. data/lib/capybara/helpers.rb +33 -0
  8. data/lib/capybara/node/actions.rb +4 -2
  9. data/lib/capybara/node/base.rb +52 -1
  10. data/lib/capybara/node/element.rb +0 -12
  11. data/lib/capybara/node/finders.rb +1 -1
  12. data/lib/capybara/node/matchers.rb +57 -47
  13. data/lib/capybara/node/simple.rb +4 -0
  14. data/lib/capybara/query.rb +11 -11
  15. data/lib/capybara/rack_test/browser.rb +14 -15
  16. data/lib/capybara/rack_test/driver.rb +2 -2
  17. data/lib/capybara/rack_test/node.rb +15 -2
  18. data/lib/capybara/result.rb +7 -19
  19. data/lib/capybara/rspec.rb +7 -4
  20. data/lib/capybara/rspec/features.rb +4 -1
  21. data/lib/capybara/rspec/matchers.rb +8 -3
  22. data/lib/capybara/selector.rb +1 -2
  23. data/lib/capybara/selenium/driver.rb +2 -2
  24. data/lib/capybara/selenium/node.rb +9 -7
  25. data/lib/capybara/session.rb +47 -31
  26. data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
  27. data/lib/capybara/spec/public/test.js +1 -1
  28. data/lib/capybara/spec/session/all_spec.rb +60 -62
  29. data/lib/capybara/spec/session/assert_selector.rb +123 -0
  30. data/lib/capybara/spec/session/attach_file_spec.rb +72 -58
  31. data/lib/capybara/spec/session/body_spec.rb +21 -0
  32. data/lib/capybara/spec/session/check_spec.rb +67 -50
  33. data/lib/capybara/spec/session/choose_spec.rb +32 -21
  34. data/lib/capybara/spec/session/click_button_spec.rb +261 -221
  35. data/lib/capybara/spec/session/click_link_or_button_spec.rb +40 -30
  36. data/lib/capybara/spec/session/click_link_spec.rb +95 -81
  37. data/lib/capybara/spec/session/current_url_spec.rb +70 -60
  38. data/lib/capybara/spec/session/evaluate_script_spec.rb +6 -0
  39. data/lib/capybara/spec/session/execute_script_spec.rb +7 -0
  40. data/lib/capybara/spec/session/fill_in_spec.rb +118 -92
  41. data/lib/capybara/spec/session/find_button_spec.rb +16 -14
  42. data/lib/capybara/spec/session/find_by_id_spec.rb +16 -14
  43. data/lib/capybara/spec/session/find_field_spec.rb +23 -21
  44. data/lib/capybara/spec/session/find_link_spec.rb +15 -14
  45. data/lib/capybara/spec/session/find_spec.rb +96 -91
  46. data/lib/capybara/spec/session/first_spec.rb +53 -55
  47. data/lib/capybara/spec/session/has_button_spec.rb +22 -24
  48. data/lib/capybara/spec/session/has_css_spec.rb +190 -205
  49. data/lib/capybara/spec/session/has_field_spec.rb +167 -169
  50. data/lib/capybara/spec/session/has_link_spec.rb +26 -29
  51. data/lib/capybara/spec/session/has_select_spec.rb +175 -176
  52. data/lib/capybara/spec/session/has_selector_spec.rb +94 -100
  53. data/lib/capybara/spec/session/has_table_spec.rb +22 -26
  54. data/lib/capybara/spec/session/has_text_spec.rb +159 -132
  55. data/lib/capybara/spec/session/has_xpath_spec.rb +100 -96
  56. data/lib/capybara/spec/session/headers.rb +4 -17
  57. data/lib/capybara/spec/session/html_spec.rb +15 -0
  58. data/lib/capybara/spec/session/node_spec.rb +172 -82
  59. data/lib/capybara/spec/session/reset_session_spec.rb +42 -0
  60. data/lib/capybara/spec/session/response_code.rb +4 -17
  61. data/lib/capybara/spec/session/save_page_spec.rb +46 -0
  62. data/lib/capybara/spec/session/screenshot.rb +8 -24
  63. data/lib/capybara/spec/session/select_spec.rb +100 -89
  64. data/lib/capybara/spec/session/source_spec.rb +12 -0
  65. data/lib/capybara/spec/session/text_spec.rb +15 -17
  66. data/lib/capybara/spec/session/uncheck_spec.rb +22 -17
  67. data/lib/capybara/spec/session/unselect_spec.rb +57 -52
  68. data/lib/capybara/spec/session/visit_spec.rb +58 -60
  69. data/lib/capybara/spec/session/within_frame_spec.rb +24 -26
  70. data/lib/capybara/spec/session/within_spec.rb +119 -121
  71. data/lib/capybara/spec/session/within_window_spec.rb +29 -31
  72. data/lib/capybara/spec/spec_helper.rb +84 -0
  73. data/lib/capybara/spec/test_app.rb +5 -3
  74. data/lib/capybara/spec/views/form.erb +1 -0
  75. data/lib/capybara/spec/views/with_html.erb +6 -1
  76. data/lib/capybara/spec/views/with_js.erb +1 -0
  77. data/lib/capybara/version.rb +1 -1
  78. data/spec/basic_node_spec.rb +2 -2
  79. data/spec/capybara_spec.rb +9 -0
  80. data/spec/dsl_spec.rb +22 -10
  81. data/spec/rack_test_spec.rb +28 -23
  82. data/spec/result_spec.rb +51 -0
  83. data/spec/rspec/features_spec.rb +19 -0
  84. data/spec/rspec/matchers_spec.rb +6 -0
  85. data/spec/rspec_spec.rb +1 -1
  86. data/spec/selenium_spec.rb +11 -25
  87. data/spec/server_spec.rb +2 -2
  88. data/spec/spec_helper.rb +1 -46
  89. metadata +41 -98
  90. data/lib/capybara/spec/session.rb +0 -183
  91. data/lib/capybara/spec/session/javascript.rb +0 -290
  92. data/lib/capybara/util/save_and_open_page.rb +0 -45
  93. data/spec/save_and_open_page_spec.rb +0 -155
@@ -126,6 +126,10 @@ module Capybara
126
126
  # no op
127
127
  end
128
128
 
129
+ def unsynchronized
130
+ yield # simple nodes don't need to wait
131
+ end
132
+
129
133
  def all(*args)
130
134
  query = Capybara::Query.new(*args)
131
135
  elements = native.xpath(query.xpath).map do |node|
@@ -11,7 +11,7 @@ module Capybara
11
11
  @options[:visible] = Capybara.ignore_hidden_elements
12
12
  end
13
13
 
14
- if args[1]
14
+ if args[0].is_a?(Symbol)
15
15
  @selector = Selector.all[args[0]]
16
16
  @locator = args[1]
17
17
  else
@@ -35,21 +35,21 @@ module Capybara
35
35
  end
36
36
 
37
37
  def matches_filters?(node)
38
- if options[:text]
39
- regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text])
40
- return false if not node.text.match(regexp)
38
+ node.unsynchronized do
39
+ if options[:text]
40
+ regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text])
41
+ return false if not node.text.match(regexp)
42
+ end
43
+ return false if options[:visible] and not node.visible?
44
+ selector.custom_filters.each do |name, block|
45
+ return false if options.has_key?(name) and not block.call(node, options[name])
46
+ end
47
+ true
41
48
  end
42
- return false if options[:visible] and not node.visible?
43
- selector.custom_filters.each do |name, block|
44
- return false if options.has_key?(name) and not block.call(node, options[name])
45
- end
46
- true
47
49
  end
48
50
 
49
51
  def matches_count?(count)
50
52
  case
51
- when count.zero?
52
- false
53
53
  when options[:between]
54
54
  options[:between] === count
55
55
  when options[:count]
@@ -45,22 +45,18 @@ class Capybara::RackTest::Browser
45
45
  new_uri = URI.parse(path)
46
46
  method.downcase! unless method.is_a? Symbol
47
47
 
48
- if new_uri.host
49
- @current_host = "#{new_uri.scheme}://#{new_uri.host}"
50
- @current_host << ":#{new_uri.port}" if new_uri.port != new_uri.default_port
51
- end
48
+ new_uri.path = request_path if path.start_with?("?")
49
+ new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
50
+ new_uri.scheme ||= @current_scheme
51
+ new_uri.host ||= @current_host
52
+ new_uri.port ||= @current_port unless new_uri.default_port == @current_port
52
53
 
53
- if new_uri.relative?
54
- if path.start_with?('?')
55
- path = request_path + path
56
- elsif not path.start_with?('/')
57
- path = request_path.sub(%r(/[^/]*$), '/') + path
58
- end
59
- path = current_host + path
60
- end
54
+ @current_scheme = new_uri.scheme
55
+ @current_host = new_uri.host
56
+ @current_port = new_uri.port
61
57
 
62
58
  reset_cache!
63
- send(method, path, attributes, env.merge(options[:headers] || {}))
59
+ send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
64
60
  end
65
61
 
66
62
  def current_url
@@ -70,14 +66,17 @@ class Capybara::RackTest::Browser
70
66
  end
71
67
 
72
68
  def reset_host!
73
- @current_host = (Capybara.app_host || Capybara.default_host)
69
+ uri = URI.parse(Capybara.app_host || Capybara.default_host)
70
+ @current_scheme = uri.scheme
71
+ @current_host = uri.host
72
+ @current_port = uri.port
74
73
  end
75
74
 
76
75
  def reset_cache!
77
76
  @dom = nil
78
77
  end
79
78
 
80
- def body
79
+ def html
81
80
  dom.to_xml
82
81
  end
83
82
 
@@ -66,8 +66,8 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
66
66
  browser.find(selector)
67
67
  end
68
68
 
69
- def body
70
- browser.body
69
+ def html
70
+ browser.html
71
71
  end
72
72
 
73
73
  def source
@@ -1,6 +1,6 @@
1
1
  class Capybara::RackTest::Node < Capybara::Driver::Node
2
2
  def text
3
- unnormalized_text.strip.gsub(/\s+/, ' ')
3
+ Capybara::Helpers.normalize_whitespace(unnormalized_text)
4
4
  end
5
5
 
6
6
  def [](name)
@@ -12,6 +12,9 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
12
12
  end
13
13
 
14
14
  def set(value)
15
+ if (Array === value) && !self[:multiple]
16
+ raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
17
+ end
15
18
  if tag_name == 'input' and type == 'radio'
16
19
  other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
17
20
  driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
@@ -29,7 +32,17 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
29
32
  # Firefox, allowing no input
30
33
  value = value[0...self[:maxlength].to_i]
31
34
  end
32
- native['value'] = value.to_s
35
+ if Array === value #Assert multiple attribute is present
36
+ value.each do |v|
37
+ new_native = native.clone
38
+ new_native.remove_attribute('value')
39
+ native.add_next_sibling(new_native)
40
+ new_native['value'] = v.to_s
41
+ end
42
+ native.remove
43
+ else
44
+ native['value'] = value.to_s
45
+ end
33
46
  elsif tag_name == "textarea"
34
47
  native.content = value.to_s
35
48
  end
@@ -1,6 +1,9 @@
1
+ require 'forwardable'
2
+
1
3
  module Capybara
2
4
  class Result
3
5
  include Enumerable
6
+ extend Forwardable
4
7
 
5
8
  def initialize(elements, query)
6
9
  @elements = elements
@@ -9,31 +12,21 @@ module Capybara
9
12
  @query = query
10
13
  end
11
14
 
12
- def each(&block)
13
- @result.each(&block)
14
- end
15
-
16
- def first
17
- @result.first
18
- end
15
+ def_delegators :@result, :each, :[], :at, :size, :count, :length, :first, :last, :empty?
19
16
 
20
17
  def matches_count?
21
18
  @query.matches_count?(@result.size)
22
19
  end
23
20
 
24
21
  def find!
25
- raise find_error if @result.count != 1
22
+ raise find_error if @result.size != 1
26
23
  @result.first
27
24
  end
28
25
 
29
- def size; @result.size; end
30
- alias_method :length, :size
31
- alias_method :count, :size
32
-
33
26
  def find_error
34
- if @result.count == 0
27
+ if @result.size == 0
35
28
  Capybara::ElementNotFound.new("Unable to find #{@query.description}")
36
- elsif @result.count > 1
29
+ elsif @result.size > 1
37
30
  Capybara::Ambiguous.new("Ambiguous match, found #{size} elements matching #{@query.description}")
38
31
  end
39
32
  end
@@ -66,11 +59,6 @@ module Capybara
66
59
  "expected not to find #{@query.description}, but there #{declension("was", "were")} #{count} #{declension("match", "matches")}"
67
60
  end
68
61
 
69
- def empty?
70
- @result.empty?
71
- end
72
- def [](key); @result[key]; end
73
-
74
62
  private
75
63
 
76
64
  def declension(singular, plural, count=count)
@@ -5,10 +5,8 @@ require 'capybara/rspec/matchers'
5
5
  require 'capybara/rspec/features'
6
6
 
7
7
  RSpec.configure do |config|
8
- config.include Capybara::DSL, :type => :request
9
- config.include Capybara::DSL, :type => :acceptance
10
- config.include Capybara::RSpecMatchers, :type => :request
11
- config.include Capybara::RSpecMatchers, :type => :acceptance
8
+ config.include Capybara::DSL, :type => :feature
9
+ config.include Capybara::RSpecMatchers, :type => :feature
12
10
  # The before and after blocks must run instantaneously, because Capybara
13
11
  # might not actually be used in all examples where it's included.
14
12
  config.after do
@@ -24,3 +22,8 @@ RSpec.configure do |config|
24
22
  end
25
23
  end
26
24
  end
25
+
26
+ # Override default rack_test driver to respect data-method attributes.
27
+ Capybara.register_driver :rack_test do |app|
28
+ Capybara::RackTest::Driver.new(app, :respect_data_method => true)
29
+ end
@@ -4,6 +4,9 @@ module Capybara
4
4
  base.instance_eval do
5
5
  alias :background :before
6
6
  alias :scenario :it
7
+ alias :xscenario :xit
8
+ alias :given :let
9
+ alias :given! :let!
7
10
  end
8
11
  end
9
12
  end
@@ -12,7 +15,7 @@ end
12
15
  def self.feature(*args, &block)
13
16
  options = if args.last.is_a?(Hash) then args.pop else {} end
14
17
  options[:capybara_feature] = true
15
- options[:type] = :request
18
+ options[:type] = :feature
16
19
  options[:caller] ||= caller
17
20
  args.push(options)
18
21
 
@@ -48,15 +48,15 @@ module Capybara
48
48
  end
49
49
 
50
50
  def failure_message_for_should
51
- "expected there to be text #{text.inspect} in #{@actual.text.inspect}"
51
+ "expected there to be text #{format(text)} in #{format(@actual.text)}"
52
52
  end
53
53
 
54
54
  def failure_message_for_should_not
55
- "expected there not to be text #{text.inspect} in #{@actual.text.inspect}"
55
+ "expected there not to be text #{format(text)} in #{format(@actual.text)}"
56
56
  end
57
57
 
58
58
  def description
59
- "have text #{text.inspect}"
59
+ "have text #{format(text)}"
60
60
  end
61
61
 
62
62
  def wrap(actual)
@@ -66,6 +66,11 @@ module Capybara
66
66
  Capybara.string(actual.to_s)
67
67
  end
68
68
  end
69
+
70
+ def format(text)
71
+ text = Capybara::Helpers.normalize_whitespace(text) unless text.is_a? Regexp
72
+ text.inspect
73
+ end
69
74
  end
70
75
 
71
76
  def have_selector(*args)
@@ -72,7 +72,6 @@ end
72
72
 
73
73
  Capybara.add_selector(:id) do
74
74
  xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
75
- match { |value| value.is_a?(Symbol) }
76
75
  end
77
76
 
78
77
  Capybara.add_selector(:field) do
@@ -95,7 +94,7 @@ end
95
94
  Capybara.add_selector(:link) do
96
95
  xpath { |locator| XPath::HTML.link(locator) }
97
96
  filter(:href) do |node, href|
98
- node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href)])
97
+ node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)])
99
98
  end
100
99
  end
101
100
 
@@ -38,7 +38,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
38
38
  browser.page_source
39
39
  end
40
40
 
41
- def body
41
+ def html
42
42
  browser.page_source
43
43
  end
44
44
 
@@ -113,6 +113,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
113
113
  end
114
114
 
115
115
  def invalid_element_errors
116
- [Selenium::WebDriver::Error::ObsoleteElementError, Selenium::WebDriver::Error::UnhandledError]
116
+ [Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledError]
117
117
  end
118
118
  end
@@ -1,6 +1,7 @@
1
1
  class Capybara::Selenium::Node < Capybara::Driver::Node
2
2
  def text
3
- native.text
3
+ # Selenium doesn't normalize Unicode whitespace.
4
+ Capybara::Helpers.normalize_whitespace(native.text)
4
5
  end
5
6
 
6
7
  def [](name)
@@ -18,12 +19,18 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
18
19
  end
19
20
 
20
21
  def set(value)
22
+ tag_name = self.tag_name
23
+ type = self[:type]
24
+ if (Array === value) && !self[:multiple]
25
+ raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
26
+ end
21
27
  if tag_name == 'input' and type == 'radio'
22
28
  click
23
29
  elsif tag_name == 'input' and type == 'checkbox'
24
30
  click if value ^ native.attribute('checked').to_s.eql?("true")
25
31
  elsif tag_name == 'input' and type == 'file'
26
- native.send_keys(value.to_s)
32
+ path_names = value.to_s.empty? ? [] : value
33
+ native.send_keys(*path_names)
27
34
  elsif tag_name == 'textarea' or tag_name == 'input'
28
35
  native.send_keys(("\b" * native[:value].size) + value.to_s)
29
36
  end
@@ -74,9 +81,4 @@ private
74
81
  def select_node
75
82
  find('./ancestor::select').first
76
83
  end
77
-
78
- def type
79
- self[:type]
80
- end
81
-
82
84
  end
@@ -29,19 +29,18 @@ module Capybara
29
29
  :click_link_or_button, :click_button, :click_link, :field_labeled,
30
30
  :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link,
31
31
  :has_content?, :has_text?, :has_css?, :has_no_content?, :has_no_text?,
32
- :has_no_css?, :has_no_xpath?, :resolve,
33
- :has_xpath?, :select, :uncheck, :has_link?, :has_no_link?, :has_button?,
34
- :has_no_button?, :has_field?, :has_no_field?, :has_checked_field?,
35
- :has_unchecked_field?, :has_no_table?, :has_table?, :unselect,
36
- :has_select?, :has_no_select?, :has_selector?, :has_no_selector?,
37
- :click_on, :has_no_checked_field?, :has_no_unchecked_field?, :query,
38
- :assert_selector, :assert_no_selector
32
+ :has_no_css?, :has_no_xpath?, :resolve, :has_xpath?, :select, :uncheck,
33
+ :has_link?, :has_no_link?, :has_button?, :has_no_button?, :has_field?,
34
+ :has_no_field?, :has_checked_field?, :has_unchecked_field?,
35
+ :has_no_table?, :has_table?, :unselect, :has_select?, :has_no_select?,
36
+ :has_selector?, :has_no_selector?, :click_on, :has_no_checked_field?,
37
+ :has_no_unchecked_field?, :query, :assert_selector, :assert_no_selector
39
38
  ]
40
39
  SESSION_METHODS = [
41
40
  :body, :html, :current_url, :current_host, :evaluate_script, :source,
42
- :visit, :within, :within_fieldset, :within_table,
43
- :within_frame, :within_window, :current_path, :save_page,
44
- :save_and_open_page, :save_screenshot, :reset_session!
41
+ :visit, :within, :within_fieldset, :within_table, :within_frame,
42
+ :within_window, :current_path, :save_page, :save_and_open_page,
43
+ :save_screenshot, :reset_session!, :response_headers, :status_code
45
44
  ]
46
45
  DSL_METHODS = NODE_METHODS + SESSION_METHODS
47
46
 
@@ -101,10 +100,10 @@ module Capybara
101
100
 
102
101
  ##
103
102
  #
104
- # @return [String] A snapshot of the HTML of the current document, as it looks right now (potentially modified by JavaScript).
103
+ # @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
105
104
  #
106
105
  def html
107
- driver.body
106
+ driver.html
108
107
  end
109
108
 
110
109
  ##
@@ -150,14 +149,22 @@ module Capybara
150
149
  # session.visit('/foo')
151
150
  # session.visit('http://google.com')
152
151
  #
153
- # For drivers which can run against an external application, such as culerity and selenium
152
+ # For drivers which can run against an external application, such as the selenium driver
154
153
  # giving an absolute URL will navigate to that page. This allows testing applications
155
- # running on remote servers. For these drivers, setting Capybara.app_host will make the
154
+ # running on remote servers. For these drivers, setting {Capybara.app_host} will make the
156
155
  # remote server the default. For example:
157
156
  #
158
157
  # Capybara.app_host = 'http://google.com'
159
158
  # session.visit('/') # visits the google homepage
160
159
  #
160
+ # If {Capybara.always_include_port} is set to true and this session is running against
161
+ # a rack application, then the port that the rack application is running on will automatically
162
+ # be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
163
+ #
164
+ # visit("http://google.com/test")
165
+ #
166
+ # Will actually navigate to `http://google.com:4567/test`.
167
+ #
161
168
  # @param [String] url The URL to navigate to
162
169
  #
163
170
  def visit(url)
@@ -203,11 +210,7 @@ module Capybara
203
210
  # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
204
211
  #
205
212
  def within(*args)
206
- new_scope = if args.size == 1 && Capybara::Node::Base === args.first
207
- args.first
208
- else
209
- find(*args)
210
- end
213
+ new_scope = if args.first.is_a?(Capybara::Node::Base) then args.first else find(*args) end
211
214
  begin
212
215
  scopes.push(new_scope)
213
216
  yield
@@ -293,16 +296,31 @@ module Capybara
293
296
 
294
297
  ##
295
298
  #
296
- # Save a snapshot of the page and open it in a browser for inspection
299
+ # Save a snapshot of the page.
297
300
  #
298
- def save_page(file_name=nil)
299
- require 'capybara/util/save_and_open_page'
300
- Capybara.save_page(body, file_name)
301
+ # @param [String] path The path to where it should be saved [optional]
302
+ #
303
+ def save_page(path=nil)
304
+ path ||= "capybara-#{Time.new.strftime("%Y%m%d%H%M%S")}#{rand(10**10)}.html"
305
+ path = File.expand_path(path, Capybara.save_and_open_page_path) if Capybara.save_and_open_page_path
306
+
307
+ FileUtils.mkdir_p(File.dirname(path))
308
+
309
+ File.open(path,'w') { |f| f.write(body) }
310
+ path
301
311
  end
302
312
 
313
+ ##
314
+ #
315
+ # Save a snapshot of the page and open it in a browser for inspection
316
+ #
317
+ # @param [String] path The path to where it should be saved [optional]
318
+ #
303
319
  def save_and_open_page(file_name=nil)
304
- require 'capybara/util/save_and_open_page'
305
- Capybara.save_and_open_page(body, file_name)
320
+ require "launchy"
321
+ Launchy.open(save_page(file_name))
322
+ rescue LoadError
323
+ warn "Please install the launchy gem to open page with save_and_open_page"
306
324
  end
307
325
 
308
326
  ##
@@ -320,12 +338,10 @@ module Capybara
320
338
  end
321
339
 
322
340
  NODE_METHODS.each do |method|
323
- class_eval <<-RUBY, __FILE__, __LINE__+1
324
- def #{method}(*args, &block)
325
- @touched = true
326
- current_node.send(:#{method}, *args, &block)
327
- end
328
- RUBY
341
+ define_method method do |*args, &block|
342
+ @touched = true
343
+ current_node.send(method, *args, &block)
344
+ end
329
345
  end
330
346
 
331
347
  def inspect