capybara 2.0.0.beta2 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
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