imseng-capybara-webkit 0.12.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.
Files changed (120) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +47 -0
  5. data/ChangeLog +70 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +19 -0
  8. data/NEWS.md +36 -0
  9. data/README.md +114 -0
  10. data/Rakefile +65 -0
  11. data/bin/Info.plist +22 -0
  12. data/capybara-webkit.gemspec +28 -0
  13. data/extconf.rb +2 -0
  14. data/gemfiles/1.0.gemfile +7 -0
  15. data/gemfiles/1.0.gemfile.lock +70 -0
  16. data/gemfiles/1.1.gemfile +7 -0
  17. data/gemfiles/1.1.gemfile.lock +70 -0
  18. data/lib/capybara/driver/webkit/browser.rb +164 -0
  19. data/lib/capybara/driver/webkit/connection.rb +120 -0
  20. data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
  21. data/lib/capybara/driver/webkit/node.rb +118 -0
  22. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  23. data/lib/capybara/driver/webkit/version.rb +7 -0
  24. data/lib/capybara/driver/webkit.rb +136 -0
  25. data/lib/capybara/webkit/matchers.rb +37 -0
  26. data/lib/capybara/webkit.rb +13 -0
  27. data/lib/capybara-webkit.rb +1 -0
  28. data/lib/capybara_webkit_builder.rb +68 -0
  29. data/spec/browser_spec.rb +173 -0
  30. data/spec/capybara_webkit_builder_spec.rb +37 -0
  31. data/spec/connection_spec.rb +54 -0
  32. data/spec/cookie_jar_spec.rb +48 -0
  33. data/spec/driver_rendering_spec.rb +80 -0
  34. data/spec/driver_resize_window_spec.rb +59 -0
  35. data/spec/driver_spec.rb +1552 -0
  36. data/spec/integration/driver_spec.rb +20 -0
  37. data/spec/integration/session_spec.rb +137 -0
  38. data/spec/self_signed_ssl_cert.rb +42 -0
  39. data/spec/spec_helper.rb +46 -0
  40. data/src/Body.h +12 -0
  41. data/src/ClearCookies.cpp +15 -0
  42. data/src/ClearCookies.h +11 -0
  43. data/src/Command.cpp +19 -0
  44. data/src/Command.h +31 -0
  45. data/src/CommandFactory.cpp +37 -0
  46. data/src/CommandFactory.h +16 -0
  47. data/src/CommandParser.cpp +76 -0
  48. data/src/CommandParser.h +33 -0
  49. data/src/Connection.cpp +71 -0
  50. data/src/Connection.h +37 -0
  51. data/src/ConsoleMessages.cpp +10 -0
  52. data/src/ConsoleMessages.h +12 -0
  53. data/src/CurrentUrl.cpp +68 -0
  54. data/src/CurrentUrl.h +16 -0
  55. data/src/Evaluate.cpp +84 -0
  56. data/src/Evaluate.h +22 -0
  57. data/src/Execute.cpp +16 -0
  58. data/src/Execute.h +12 -0
  59. data/src/Find.cpp +19 -0
  60. data/src/Find.h +13 -0
  61. data/src/FrameFocus.cpp +66 -0
  62. data/src/FrameFocus.h +28 -0
  63. data/src/GetCookies.cpp +20 -0
  64. data/src/GetCookies.h +14 -0
  65. data/src/Header.cpp +18 -0
  66. data/src/Header.h +11 -0
  67. data/src/Headers.cpp +10 -0
  68. data/src/Headers.h +12 -0
  69. data/src/IgnoreSslErrors.cpp +12 -0
  70. data/src/IgnoreSslErrors.h +12 -0
  71. data/src/JavascriptInvocation.cpp +14 -0
  72. data/src/JavascriptInvocation.h +19 -0
  73. data/src/NetworkAccessManager.cpp +29 -0
  74. data/src/NetworkAccessManager.h +19 -0
  75. data/src/NetworkCookieJar.cpp +101 -0
  76. data/src/NetworkCookieJar.h +15 -0
  77. data/src/Node.cpp +14 -0
  78. data/src/Node.h +13 -0
  79. data/src/NullCommand.cpp +10 -0
  80. data/src/NullCommand.h +11 -0
  81. data/src/PageLoadingCommand.cpp +46 -0
  82. data/src/PageLoadingCommand.h +40 -0
  83. data/src/Render.cpp +18 -0
  84. data/src/Render.h +12 -0
  85. data/src/RequestedUrl.cpp +12 -0
  86. data/src/RequestedUrl.h +12 -0
  87. data/src/Reset.cpp +29 -0
  88. data/src/Reset.h +15 -0
  89. data/src/ResizeWindow.cpp +16 -0
  90. data/src/ResizeWindow.h +12 -0
  91. data/src/Response.cpp +24 -0
  92. data/src/Response.h +15 -0
  93. data/src/Server.cpp +24 -0
  94. data/src/Server.h +21 -0
  95. data/src/SetCookie.cpp +16 -0
  96. data/src/SetCookie.h +11 -0
  97. data/src/SetProxy.cpp +22 -0
  98. data/src/SetProxy.h +11 -0
  99. data/src/Source.cpp +18 -0
  100. data/src/Source.h +19 -0
  101. data/src/Status.cpp +12 -0
  102. data/src/Status.h +12 -0
  103. data/src/UnsupportedContentHandler.cpp +32 -0
  104. data/src/UnsupportedContentHandler.h +18 -0
  105. data/src/Url.cpp +12 -0
  106. data/src/Url.h +12 -0
  107. data/src/Visit.cpp +12 -0
  108. data/src/Visit.h +12 -0
  109. data/src/WebPage.cpp +239 -0
  110. data/src/WebPage.h +58 -0
  111. data/src/body.cpp +10 -0
  112. data/src/capybara.js +315 -0
  113. data/src/find_command.h +29 -0
  114. data/src/main.cpp +33 -0
  115. data/src/webkit_server.pro +85 -0
  116. data/src/webkit_server.qrc +5 -0
  117. data/templates/Command.cpp +10 -0
  118. data/templates/Command.h +12 -0
  119. data/webkit_server.pro +4 -0
  120. metadata +298 -0
@@ -0,0 +1,164 @@
1
+ require 'json'
2
+
3
+ class Capybara::Driver::Webkit
4
+ class Browser
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ def visit(url)
10
+ command "Visit", url
11
+ end
12
+
13
+ def header(key, value)
14
+ command("Header", key, value)
15
+ end
16
+
17
+ def find(query)
18
+ command("Find", query).split(",")
19
+ end
20
+
21
+ def reset!
22
+ command("Reset")
23
+ end
24
+
25
+ def body
26
+ command("Body")
27
+ end
28
+
29
+ def source
30
+ command("Source")
31
+ end
32
+
33
+ def status_code
34
+ command("Status").to_i
35
+ end
36
+
37
+ def console_messages
38
+ command("ConsoleMessages").split("\n").map do |messages|
39
+ parts = messages.split("|", 3)
40
+ { :source => parts.first, :line_number => Integer(parts[1]), :message => parts.last }
41
+ end
42
+ end
43
+
44
+ def error_messages
45
+ console_messages.select do |message|
46
+ message[:message] =~ /Error:/
47
+ end
48
+ end
49
+
50
+ def response_headers
51
+ Hash[command("Headers").split("\n").map { |header| header.split(": ") }]
52
+ end
53
+
54
+ def url
55
+ command("Url")
56
+ end
57
+
58
+ def requested_url
59
+ command("RequestedUrl")
60
+ end
61
+
62
+ def current_url
63
+ command("CurrentUrl")
64
+ end
65
+
66
+ def frame_focus(frame_id_or_index=nil)
67
+ if frame_id_or_index.is_a? Fixnum
68
+ command("FrameFocus", "", frame_id_or_index.to_s)
69
+ elsif frame_id_or_index
70
+ command("FrameFocus", frame_id_or_index)
71
+ else
72
+ command("FrameFocus")
73
+ end
74
+ end
75
+
76
+ def ignore_ssl_errors
77
+ command("IgnoreSslErrors")
78
+ end
79
+
80
+ def command(name, *args)
81
+ @connection.puts name
82
+ @connection.puts args.size
83
+ args.each do |arg|
84
+ @connection.puts arg.to_s.bytesize
85
+ @connection.print arg.to_s
86
+ end
87
+ check
88
+ read_response
89
+ end
90
+
91
+ def evaluate_script(script)
92
+ json = command('Evaluate', script)
93
+ JSON.parse("[#{json}]").first
94
+ end
95
+
96
+ def execute_script(script)
97
+ command('Execute', script)
98
+ end
99
+
100
+ def render(path, width, height)
101
+ command "Render", path, width, height
102
+ end
103
+
104
+ def set_cookie(cookie)
105
+ command "SetCookie", cookie
106
+ end
107
+
108
+ def clear_cookies
109
+ command "ClearCookies"
110
+ end
111
+
112
+ def get_cookies
113
+ command("GetCookies").lines.map{ |line| line.strip }.select{ |line| !line.empty? }
114
+ end
115
+
116
+ def set_proxy(options = {})
117
+ options = default_proxy_options.merge(options)
118
+ command("SetProxy", options[:host], options[:port], options[:user], options[:pass])
119
+ end
120
+
121
+ def clear_proxy
122
+ command("SetProxy")
123
+ end
124
+
125
+ def resize_window(width, height)
126
+ command("ResizeWindow", width.to_i, height.to_i)
127
+ end
128
+
129
+ private
130
+
131
+ def check
132
+ result = @connection.gets
133
+ result.strip! if result
134
+
135
+ if result.nil?
136
+ raise WebkitNoResponseError, "No response received from the server."
137
+ elsif result != 'ok'
138
+ raise WebkitInvalidResponseError, read_response
139
+ end
140
+
141
+ result
142
+ end
143
+
144
+ def read_response
145
+ response_length = @connection.gets.to_i
146
+ if response_length > 0
147
+ response = @connection.read(response_length)
148
+ response.force_encoding("UTF-8") if response.respond_to?(:force_encoding)
149
+ response
150
+ else
151
+ ""
152
+ end
153
+ end
154
+
155
+ def default_proxy_options
156
+ {
157
+ :host => "localhost",
158
+ :port => "0",
159
+ :user => "",
160
+ :pass => ""
161
+ }
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,120 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+ require 'thread'
4
+
5
+ class Capybara::Driver::Webkit
6
+ class Connection
7
+ WEBKIT_SERVER_START_TIMEOUT = 15
8
+
9
+ attr_reader :port
10
+
11
+ def initialize(options = {})
12
+ @socket_class = options[:socket_class] || TCPSocket
13
+ @stdout = options.has_key?(:stdout) ?
14
+ options[:stdout] :
15
+ $stdout
16
+ start_server
17
+ connect
18
+ end
19
+
20
+ def puts(string)
21
+ @socket.puts string
22
+ end
23
+
24
+ def print(string)
25
+ @socket.print string
26
+ end
27
+
28
+ def gets
29
+ @socket.gets
30
+ end
31
+
32
+ def read(length)
33
+ @socket.read(length)
34
+ end
35
+
36
+ private
37
+
38
+ def start_server
39
+ pipe = fork_server
40
+ @port = discover_port(pipe)
41
+ @stdout_thread = Thread.new do
42
+ Thread.current.abort_on_exception = true
43
+ forward_stdout(pipe)
44
+ end
45
+ end
46
+
47
+ def fork_server
48
+ server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
49
+ pipe, @pid = server_pipe_and_pid(server_path)
50
+ register_shutdown_hook
51
+ pipe
52
+ end
53
+
54
+ def kill_process(pid)
55
+ if RUBY_PLATFORM =~ /mingw32/
56
+ Process.kill(9, pid)
57
+ else
58
+ Process.kill("INT", pid)
59
+ end
60
+ end
61
+
62
+ def register_shutdown_hook
63
+ @owner_pid = Process.pid
64
+ at_exit do
65
+ if Process.pid == @owner_pid
66
+ kill_process(@pid)
67
+ end
68
+ end
69
+ end
70
+
71
+ def server_pipe_and_pid(server_path)
72
+ cmdline = [server_path]
73
+ pipe = IO.popen(cmdline.join(" "))
74
+ [pipe, pipe.pid]
75
+ end
76
+
77
+ def discover_port(read_pipe)
78
+ return unless IO.select([read_pipe], nil, nil, WEBKIT_SERVER_START_TIMEOUT)
79
+ ((read_pipe.first || '').match(/listening on port: (\d+)/) || [])[1].to_i
80
+ end
81
+
82
+ def forward_stdout(pipe)
83
+ while pipe_readable?(pipe)
84
+ line = pipe.readline
85
+ if @stdout
86
+ @stdout.write(line)
87
+ @stdout.flush
88
+ end
89
+ end
90
+ rescue EOFError
91
+ end
92
+
93
+ def connect
94
+ Timeout.timeout(5) do
95
+ while @socket.nil?
96
+ attempt_connect
97
+ end
98
+ end
99
+ end
100
+
101
+ def attempt_connect
102
+ @socket = @socket_class.open("127.0.0.1", @port)
103
+ rescue Errno::ECONNREFUSED
104
+ end
105
+
106
+ if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == "ruby" && RUBY_VERSION <= "1.8")
107
+ # please note the use of IO::select() here, as it is used specifically to
108
+ # preserve correct signal handling behavior in ruby 1.8.
109
+ # https://github.com/thibaudgg/rb-fsevent/commit/d1a868bf8dc72dbca102bedbadff76c7e6c2dc21
110
+ # https://github.com/thibaudgg/rb-fsevent/blob/1ca42b987596f350ee7b19d8f8210b7b6ae8766b/ext/fsevent/fsevent_watch.c#L171
111
+ def pipe_readable?(pipe)
112
+ IO.select([pipe])
113
+ end
114
+ else
115
+ def pipe_readable?(pipe)
116
+ !pipe.eof?
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,55 @@
1
+ require 'webrick'
2
+
3
+ # A simple cookie jar implementation.
4
+ # Does not take special cookie attributes
5
+ # into account like expire, max-age, httponly, secure
6
+ class Capybara::Driver::Webkit::CookieJar
7
+ attr_reader :browser
8
+
9
+ def initialize(browser)
10
+ @browser = browser
11
+ end
12
+
13
+ def [](*args)
14
+ cookie = find(*args)
15
+ cookie && cookie.value
16
+ end
17
+
18
+ def find(name, domain = nil, path = "/")
19
+ # we are sorting by path size because more specific paths take
20
+ # precendence
21
+ cookies.sort_by { |c| -c.path.size }.find { |c|
22
+ c.name.downcase == name.downcase &&
23
+ (!domain || valid_domain?(c, domain)) &&
24
+ (!path || valid_path?(c, path))
25
+ }
26
+ end
27
+
28
+ protected
29
+
30
+ def valid_domain?(cookie, domain)
31
+ ends_with?(("." + domain).downcase,
32
+ normalize_domain(cookie.domain).downcase)
33
+ end
34
+
35
+ def normalize_domain(domain)
36
+ domain = "." + domain unless domain[0,1] == "."
37
+ domain
38
+ end
39
+
40
+ def valid_path?(cookie, path)
41
+ starts_with?(path, cookie.path)
42
+ end
43
+
44
+ def ends_with?(str, suffix)
45
+ str[-suffix.size..-1] == suffix
46
+ end
47
+
48
+ def starts_with?(str, prefix)
49
+ str[0, prefix.size] == prefix
50
+ end
51
+
52
+ def cookies
53
+ browser.get_cookies.map { |c| WEBrick::Cookie.parse_set_cookie(c) }
54
+ end
55
+ end
@@ -0,0 +1,118 @@
1
+ class Capybara::Driver::Webkit
2
+ class Node < Capybara::Driver::Node
3
+ NBSP = "\xC2\xA0"
4
+ NBSP.force_encoding("UTF-8") if NBSP.respond_to?(:force_encoding)
5
+
6
+ def text
7
+ invoke("text").gsub(NBSP, ' ').gsub(/\s+/u, ' ').strip
8
+ end
9
+
10
+ def [](name)
11
+ value = invoke("attribute", name)
12
+ if name == 'checked' || name == 'disabled'
13
+ value == 'true'
14
+ else
15
+ value
16
+ end
17
+ end
18
+
19
+ def value
20
+ if multiple_select?
21
+ self.find(".//option").select(&:selected?).map(&:value)
22
+ else
23
+ invoke "value"
24
+ end
25
+ end
26
+
27
+ def set(value)
28
+ invoke "set", value
29
+ end
30
+
31
+ def select_option
32
+ invoke "selectOption"
33
+ end
34
+
35
+ def unselect_option
36
+ select = find("ancestor::select").first
37
+ if select.multiple_select?
38
+ invoke "unselectOption"
39
+ else
40
+ raise Capybara::UnselectNotAllowed
41
+ end
42
+ end
43
+
44
+ def click
45
+ invoke "click"
46
+ end
47
+
48
+ def drag_to(element)
49
+ invoke 'dragTo', element.native
50
+ end
51
+
52
+ def tag_name
53
+ invoke "tagName"
54
+ end
55
+
56
+ def visible?
57
+ invoke("visible") == "true"
58
+ end
59
+
60
+ def selected?
61
+ invoke("selected") == "true"
62
+ end
63
+
64
+ def checked?
65
+ self['checked']
66
+ end
67
+
68
+ def disabled?
69
+ self['disabled']
70
+ end
71
+
72
+ def path
73
+ invoke "path"
74
+ end
75
+
76
+ def submit(opts = {})
77
+ invoke "submit"
78
+ end
79
+
80
+ def trigger(event)
81
+ invoke "trigger", event
82
+ end
83
+
84
+ def find(xpath)
85
+ invoke("findWithin", xpath).split(',').map do |native|
86
+ self.class.new(driver, native)
87
+ end
88
+ end
89
+
90
+ def invoke(name, *args)
91
+ if allow_unattached_nodes? || attached?
92
+ browser.command "Node", name, native, *args
93
+ else
94
+ raise Capybara::Driver::Webkit::NodeNotAttachedError
95
+ end
96
+ end
97
+
98
+ def allow_unattached_nodes?
99
+ !automatic_reload?
100
+ end
101
+
102
+ def automatic_reload?
103
+ Capybara.respond_to?(:automatic_reload) && Capybara.automatic_reload
104
+ end
105
+
106
+ def attached?
107
+ browser.command("Node", "isAttached", native) == "true"
108
+ end
109
+
110
+ def browser
111
+ driver.browser
112
+ end
113
+
114
+ def multiple_select?
115
+ self.tag_name == "select" && self["multiple"] == "multiple"
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,43 @@
1
+ # Wraps the TCP socket and prints data sent and received. Used for debugging
2
+ # the wire protocol. You can use this by passing a :socket_class to Browser.
3
+ class Capybara::Driver::Webkit
4
+ class SocketDebugger
5
+ def self.open(host, port)
6
+ real_socket = TCPSocket.open(host, port)
7
+ new(real_socket)
8
+ end
9
+
10
+ def initialize(socket)
11
+ @socket = socket
12
+ end
13
+
14
+ def read(length)
15
+ received @socket.read(length)
16
+ end
17
+
18
+ def puts(line)
19
+ sent line
20
+ @socket.puts(line)
21
+ end
22
+
23
+ def print(content)
24
+ sent content
25
+ @socket.print(content)
26
+ end
27
+
28
+ def gets
29
+ received @socket.gets
30
+ end
31
+
32
+ private
33
+
34
+ def sent(content)
35
+ Kernel.puts " >> " + content.to_s
36
+ end
37
+
38
+ def received(content)
39
+ Kernel.puts " << " + content.to_s
40
+ content
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module Capybara
2
+ module Driver
3
+ class Webkit
4
+ VERSION = '0.12.1'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,136 @@
1
+ require "capybara"
2
+ require "capybara/driver/webkit/version"
3
+ require "capybara/driver/webkit/node"
4
+ require "capybara/driver/webkit/connection"
5
+ require "capybara/driver/webkit/browser"
6
+ require "capybara/driver/webkit/socket_debugger"
7
+ require "capybara/driver/webkit/cookie_jar"
8
+
9
+ class Capybara::Driver::Webkit
10
+ class WebkitInvalidResponseError < StandardError
11
+ end
12
+
13
+ class WebkitNoResponseError < StandardError
14
+ end
15
+
16
+ class NodeNotAttachedError < Capybara::ElementNotFound
17
+ end
18
+
19
+ attr_reader :browser
20
+
21
+ def initialize(app, options={})
22
+ @app = app
23
+ @options = options
24
+ @rack_server = Capybara::Server.new(@app)
25
+ @rack_server.boot if Capybara.run_server
26
+ @browser = options[:browser] || Browser.new(Connection.new(options))
27
+ @browser.ignore_ssl_errors if options[:ignore_ssl_errors]
28
+ end
29
+
30
+ def current_url
31
+ browser.current_url
32
+ end
33
+
34
+ def requested_url
35
+ browser.requested_url
36
+ end
37
+
38
+ def visit(path)
39
+ browser.visit(url(path))
40
+ end
41
+
42
+ def find(query)
43
+ browser.find(query).map { |native| Node.new(self, native) }
44
+ end
45
+
46
+ def source
47
+ browser.source
48
+ end
49
+
50
+ def body
51
+ browser.body
52
+ end
53
+
54
+ def header(key, value)
55
+ browser.header(key, value)
56
+ end
57
+
58
+ def execute_script(script)
59
+ value = browser.execute_script script
60
+ value.empty? ? nil : value
61
+ end
62
+
63
+ def evaluate_script(script)
64
+ browser.evaluate_script script
65
+ end
66
+
67
+ def console_messages
68
+ browser.console_messages
69
+ end
70
+
71
+ def error_messages
72
+ browser.error_messages
73
+ end
74
+
75
+ def response_headers
76
+ browser.response_headers
77
+ end
78
+
79
+ def status_code
80
+ browser.status_code
81
+ end
82
+
83
+ def resize_window(width, height)
84
+ browser.resize_window(width, height)
85
+ end
86
+
87
+ def within_frame(frame_id_or_index)
88
+ browser.frame_focus(frame_id_or_index)
89
+ begin
90
+ yield
91
+ ensure
92
+ browser.frame_focus
93
+ end
94
+ end
95
+
96
+ def within_window(handle)
97
+ raise Capybara::NotSupportedByDriverError
98
+ end
99
+
100
+ def wait?
101
+ true
102
+ end
103
+
104
+ def wait_until(*args)
105
+ end
106
+
107
+ def reset!
108
+ browser.reset!
109
+ end
110
+
111
+ def has_shortcircuit_timeout?
112
+ false
113
+ end
114
+
115
+ def render(path, options={})
116
+ options[:width] ||= 1000
117
+ options[:height] ||= 10
118
+
119
+ browser.render path, options[:width], options[:height]
120
+ end
121
+
122
+ def server_port
123
+ @rack_server.port
124
+ end
125
+
126
+ def cookies
127
+ @cookie_jar ||= CookieJar.new(browser)
128
+ end
129
+
130
+ private
131
+
132
+ def url(path)
133
+ @rack_server.url(path)
134
+ end
135
+ end
136
+
@@ -0,0 +1,37 @@
1
+ module Capybara
2
+ module Webkit
3
+ module RspecMatchers
4
+ RSpec::Matchers.define :have_errors do |expected|
5
+ match do |actual|
6
+ actual = resolve(actual)
7
+ actual.error_messages.any?
8
+ end
9
+
10
+ failure_message_for_should do |actual|
11
+ "Expected Javascript errors, but there were none."
12
+ end
13
+
14
+ failure_message_for_should_not do |actual|
15
+ actual = resolve(actual)
16
+ "Expected no Javascript errors, got:\n#{error_messages_for(actual)}"
17
+ end
18
+
19
+ def error_messages_for(obj)
20
+ obj.error_messages.map do |m|
21
+ " - #{m[:message]}"
22
+ end.join("\n")
23
+ end
24
+
25
+ def resolve(actual)
26
+ if actual.respond_to? :page
27
+ actual.page.driver
28
+ elsif actual.respond_to? :driver
29
+ actual.driver
30
+ else
31
+ actual
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ require "capybara"
2
+ require "capybara/driver/webkit"
3
+
4
+ Capybara.register_driver :webkit do |app|
5
+ Capybara::Driver::Webkit.new(app)
6
+ end
7
+
8
+ Capybara.register_driver :webkit_debug do |app|
9
+ connection = Capybara::Driver::Webkit::Connection.new(
10
+ :socket_class => Capybara::Driver::Webkit::SocketDebugger)
11
+ browser = Capybara::Driver::Webkit::Browser.new(connection)
12
+ Capybara::Driver::Webkit.new(app, :browser => browser)
13
+ end
@@ -0,0 +1 @@
1
+ require "capybara/webkit"