intentmedia-capybara-webkit 0.7.2.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/.gitignore +17 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +65 -0
- data/LICENSE +19 -0
- data/README.md +67 -0
- data/Rakefile +78 -0
- data/bin/Info.plist +22 -0
- data/capybara-webkit.gemspec +24 -0
- data/extconf.rb +2 -0
- data/gemfiles/1.0.gemfile +7 -0
- data/gemfiles/1.0.gemfile.lock +65 -0
- data/gemfiles/1.1.gemfile +7 -0
- data/gemfiles/1.1.gemfile.lock +65 -0
- data/lib/capybara-webkit.rb +1 -0
- data/lib/capybara/driver/webkit.rb +113 -0
- data/lib/capybara/driver/webkit/browser.rb +216 -0
- data/lib/capybara/driver/webkit/node.rb +118 -0
- data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
- data/lib/capybara/webkit.rb +11 -0
- data/lib/capybara_webkit_builder.rb +40 -0
- data/spec/browser_spec.rb +178 -0
- data/spec/driver_rendering_spec.rb +80 -0
- data/spec/driver_spec.rb +1048 -0
- data/spec/integration/driver_spec.rb +20 -0
- data/spec/integration/session_spec.rb +137 -0
- data/spec/self_signed_ssl_cert.rb +42 -0
- data/spec/spec_helper.rb +25 -0
- data/src/Body.h +12 -0
- data/src/ClearCookies.cpp +18 -0
- data/src/ClearCookies.h +11 -0
- data/src/Command.cpp +15 -0
- data/src/Command.h +29 -0
- data/src/CommandFactory.cpp +29 -0
- data/src/CommandFactory.h +16 -0
- data/src/CommandParser.cpp +68 -0
- data/src/CommandParser.h +29 -0
- data/src/Connection.cpp +82 -0
- data/src/Connection.h +36 -0
- data/src/Evaluate.cpp +84 -0
- data/src/Evaluate.h +22 -0
- data/src/Execute.cpp +16 -0
- data/src/Execute.h +12 -0
- data/src/Find.cpp +19 -0
- data/src/Find.h +13 -0
- data/src/FrameFocus.cpp +66 -0
- data/src/FrameFocus.h +28 -0
- data/src/GetCookies.cpp +22 -0
- data/src/GetCookies.h +14 -0
- data/src/Header.cpp +18 -0
- data/src/Header.h +11 -0
- data/src/Headers.cpp +11 -0
- data/src/Headers.h +12 -0
- data/src/JavascriptInvocation.cpp +14 -0
- data/src/JavascriptInvocation.h +19 -0
- data/src/NetworkAccessManager.cpp +25 -0
- data/src/NetworkAccessManager.h +18 -0
- data/src/NetworkCookieJar.cpp +101 -0
- data/src/NetworkCookieJar.h +15 -0
- data/src/Node.cpp +14 -0
- data/src/Node.h +13 -0
- data/src/Render.cpp +19 -0
- data/src/Render.h +12 -0
- data/src/Reset.cpp +20 -0
- data/src/Reset.h +12 -0
- data/src/Response.cpp +19 -0
- data/src/Response.h +13 -0
- data/src/Server.cpp +25 -0
- data/src/Server.h +21 -0
- data/src/SetCookie.cpp +18 -0
- data/src/SetCookie.h +11 -0
- data/src/SetProxy.cpp +24 -0
- data/src/SetProxy.h +11 -0
- data/src/Source.cpp +20 -0
- data/src/Source.h +19 -0
- data/src/Status.cpp +13 -0
- data/src/Status.h +12 -0
- data/src/UnsupportedContentHandler.cpp +32 -0
- data/src/UnsupportedContentHandler.h +18 -0
- data/src/Url.cpp +15 -0
- data/src/Url.h +12 -0
- data/src/Visit.cpp +21 -0
- data/src/Visit.h +15 -0
- data/src/WebPage.cpp +226 -0
- data/src/WebPage.h +54 -0
- data/src/body.cpp +11 -0
- data/src/capybara.js +205 -0
- data/src/find_command.h +24 -0
- data/src/main.cpp +34 -0
- data/src/webkit_server.pro +71 -0
- data/src/webkit_server.qrc +5 -0
- data/templates/Command.cpp +10 -0
- data/templates/Command.h +12 -0
- data/webkit_server.pro +4 -0
- metadata +246 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /Users/jferris/Source/capybara-webkit
|
3
|
+
specs:
|
4
|
+
capybara-webkit (0.6.1)
|
5
|
+
capybara (< 1.2, >= 1.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
appraisal (0.3.8)
|
11
|
+
bundler
|
12
|
+
rake
|
13
|
+
capybara (1.1.1)
|
14
|
+
mime-types (>= 1.16)
|
15
|
+
nokogiri (>= 1.3.3)
|
16
|
+
rack (>= 1.0.0)
|
17
|
+
rack-test (>= 0.5.4)
|
18
|
+
selenium-webdriver (~> 2.0)
|
19
|
+
xpath (~> 0.1.4)
|
20
|
+
childprocess (0.2.2)
|
21
|
+
ffi (~> 1.0.6)
|
22
|
+
diff-lcs (1.1.3)
|
23
|
+
ffi (1.0.9)
|
24
|
+
json_pure (1.6.1)
|
25
|
+
mime-types (1.16)
|
26
|
+
mini_magick (3.3)
|
27
|
+
subexec (~> 0.1.0)
|
28
|
+
nokogiri (1.5.0)
|
29
|
+
rack (1.3.3)
|
30
|
+
rack-test (0.6.1)
|
31
|
+
rack (>= 1.0)
|
32
|
+
rake (0.9.2)
|
33
|
+
rspec (2.6.0)
|
34
|
+
rspec-core (~> 2.6.0)
|
35
|
+
rspec-expectations (~> 2.6.0)
|
36
|
+
rspec-mocks (~> 2.6.0)
|
37
|
+
rspec-core (2.6.4)
|
38
|
+
rspec-expectations (2.6.0)
|
39
|
+
diff-lcs (~> 1.1.2)
|
40
|
+
rspec-mocks (2.6.0)
|
41
|
+
rubyzip (0.9.4)
|
42
|
+
selenium-webdriver (2.7.0)
|
43
|
+
childprocess (>= 0.2.1)
|
44
|
+
ffi (>= 1.0.7)
|
45
|
+
json_pure
|
46
|
+
rubyzip
|
47
|
+
sinatra (1.2.6)
|
48
|
+
rack (~> 1.1)
|
49
|
+
tilt (< 2.0, >= 1.2.2)
|
50
|
+
subexec (0.1.0)
|
51
|
+
tilt (1.3.3)
|
52
|
+
xpath (0.1.4)
|
53
|
+
nokogiri (~> 1.3)
|
54
|
+
|
55
|
+
PLATFORMS
|
56
|
+
ruby
|
57
|
+
|
58
|
+
DEPENDENCIES
|
59
|
+
appraisal
|
60
|
+
capybara (~> 1.1.0)
|
61
|
+
capybara-webkit!
|
62
|
+
mini_magick
|
63
|
+
rake
|
64
|
+
rspec (~> 2.6.0)
|
65
|
+
sinatra
|
@@ -0,0 +1 @@
|
|
1
|
+
require "capybara/webkit"
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "capybara"
|
2
|
+
require "capybara/driver/webkit/node"
|
3
|
+
require "capybara/driver/webkit/browser"
|
4
|
+
require "capybara/driver/webkit/socket_debugger"
|
5
|
+
|
6
|
+
class Capybara::Driver::Webkit
|
7
|
+
class WebkitInvalidResponseError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class WebkitNoResponseError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class NodeNotAttachedError < Capybara::ElementNotFound
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :browser
|
17
|
+
|
18
|
+
def initialize(app, options={})
|
19
|
+
@app = app
|
20
|
+
@options = options
|
21
|
+
@rack_server = Capybara::Server.new(@app)
|
22
|
+
@rack_server.boot if Capybara.run_server
|
23
|
+
@browser = options[:browser] || Browser.new(
|
24
|
+
:ignore_ssl_errors => options[:ignore_ssl_errors])
|
25
|
+
end
|
26
|
+
|
27
|
+
def current_url
|
28
|
+
browser.url
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit(path)
|
32
|
+
browser.visit(url(path))
|
33
|
+
end
|
34
|
+
|
35
|
+
def find(query)
|
36
|
+
browser.find(query).map { |native| Node.new(self, native) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def source
|
40
|
+
browser.source
|
41
|
+
end
|
42
|
+
|
43
|
+
def body
|
44
|
+
browser.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def header(key, value)
|
48
|
+
browser.header(key, value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute_script(script)
|
52
|
+
value = browser.execute_script script
|
53
|
+
value.empty? ? nil : value
|
54
|
+
end
|
55
|
+
|
56
|
+
def evaluate_script(script)
|
57
|
+
browser.evaluate_script script
|
58
|
+
end
|
59
|
+
|
60
|
+
def response_headers
|
61
|
+
browser.response_headers
|
62
|
+
end
|
63
|
+
|
64
|
+
def status_code
|
65
|
+
browser.status_code
|
66
|
+
end
|
67
|
+
|
68
|
+
def within_frame(frame_id_or_index)
|
69
|
+
browser.frame_focus(frame_id_or_index)
|
70
|
+
begin
|
71
|
+
yield
|
72
|
+
ensure
|
73
|
+
browser.frame_focus
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def within_window(handle)
|
78
|
+
raise Capybara::NotSupportedByDriverError
|
79
|
+
end
|
80
|
+
|
81
|
+
def wait?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def wait_until(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def reset!
|
89
|
+
browser.reset!
|
90
|
+
end
|
91
|
+
|
92
|
+
def has_shortcircuit_timeout?
|
93
|
+
false
|
94
|
+
end
|
95
|
+
|
96
|
+
def render(path, options={})
|
97
|
+
options[:width] ||= 1000
|
98
|
+
options[:height] ||= 10
|
99
|
+
|
100
|
+
browser.render path, options[:width], options[:height]
|
101
|
+
end
|
102
|
+
|
103
|
+
def server_port
|
104
|
+
@rack_server.port
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def url(path)
|
110
|
+
@rack_server.url(path)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
require 'capybara/util/timeout'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class Capybara::Driver::Webkit
|
7
|
+
class Browser
|
8
|
+
attr :server_port
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@socket_class = options[:socket_class] || TCPSocket
|
12
|
+
@stdout = options.has_key?(:stdout) ?
|
13
|
+
options[:stdout] :
|
14
|
+
$stdout
|
15
|
+
@ignore_ssl_errors = options[:ignore_ssl_errors]
|
16
|
+
start_server
|
17
|
+
connect
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit(url)
|
21
|
+
command "Visit", url
|
22
|
+
end
|
23
|
+
|
24
|
+
def header(key, value)
|
25
|
+
command("Header", key, value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find(query)
|
29
|
+
command("Find", query).split(",")
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset!
|
33
|
+
command("Reset")
|
34
|
+
end
|
35
|
+
|
36
|
+
def body
|
37
|
+
command("Body")
|
38
|
+
end
|
39
|
+
|
40
|
+
def source
|
41
|
+
command("Source")
|
42
|
+
end
|
43
|
+
|
44
|
+
def status_code
|
45
|
+
command("Status").to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
def response_headers
|
49
|
+
Hash[command("Headers").split("\n").map { |header| header.split(": ") }]
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
command("Url")
|
54
|
+
end
|
55
|
+
|
56
|
+
def frame_focus(frame_id_or_index=nil)
|
57
|
+
if frame_id_or_index.is_a? Fixnum
|
58
|
+
command("FrameFocus", "", frame_id_or_index.to_s)
|
59
|
+
elsif frame_id_or_index
|
60
|
+
command("FrameFocus", frame_id_or_index)
|
61
|
+
else
|
62
|
+
command("FrameFocus")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def command(name, *args)
|
67
|
+
@socket.puts name
|
68
|
+
@socket.puts args.size
|
69
|
+
args.each do |arg|
|
70
|
+
@socket.puts arg.to_s.bytesize
|
71
|
+
@socket.print arg.to_s
|
72
|
+
end
|
73
|
+
check
|
74
|
+
read_response
|
75
|
+
end
|
76
|
+
|
77
|
+
def evaluate_script(script)
|
78
|
+
json = command('Evaluate', script)
|
79
|
+
JSON.parse("[#{json}]").first
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute_script(script)
|
83
|
+
command('Execute', script)
|
84
|
+
end
|
85
|
+
|
86
|
+
def render(path, width, height)
|
87
|
+
command "Render", path, width, height
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_cookie(cookie)
|
91
|
+
command "SetCookie", cookie
|
92
|
+
end
|
93
|
+
|
94
|
+
def clear_cookies
|
95
|
+
command "ClearCookies"
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_cookies
|
99
|
+
command("GetCookies").lines.map{ |line| line.strip }.select{ |line| !line.empty? }
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_proxy(options = {})
|
103
|
+
options = default_proxy_options.merge(options)
|
104
|
+
command("SetProxy", options[:host], options[:port], options[:user], options[:pass])
|
105
|
+
end
|
106
|
+
|
107
|
+
def clear_proxy
|
108
|
+
command("SetProxy")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def start_server
|
114
|
+
pipe = fork_server
|
115
|
+
@server_port = discover_server_port(pipe)
|
116
|
+
@stdout_thread = Thread.new do
|
117
|
+
Thread.current.abort_on_exception = true
|
118
|
+
forward_stdout(pipe)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def fork_server
|
123
|
+
server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
|
124
|
+
pipe, @pid = server_pipe_and_pid(server_path)
|
125
|
+
register_shutdown_hook
|
126
|
+
pipe
|
127
|
+
end
|
128
|
+
|
129
|
+
def register_shutdown_hook
|
130
|
+
@owner_pid = Process.pid
|
131
|
+
at_exit do
|
132
|
+
if Process.pid == @owner_pid
|
133
|
+
Process.kill("INT", @pid)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def server_pipe_and_pid(server_path)
|
139
|
+
cmdline = [server_path]
|
140
|
+
cmdline << "--ignore-ssl-errors" if @ignore_ssl_errors
|
141
|
+
pipe = IO.popen(cmdline.join(" "))
|
142
|
+
[pipe, pipe.pid]
|
143
|
+
end
|
144
|
+
|
145
|
+
def discover_server_port(read_pipe)
|
146
|
+
return unless IO.select([read_pipe], nil, nil, 10)
|
147
|
+
((read_pipe.first || '').match(/listening on port: (\d+)/) || [])[1].to_i
|
148
|
+
end
|
149
|
+
|
150
|
+
def forward_stdout(pipe)
|
151
|
+
while pipe_readable?(pipe)
|
152
|
+
line = pipe.readline
|
153
|
+
if @stdout
|
154
|
+
@stdout.write(line)
|
155
|
+
@stdout.flush
|
156
|
+
end
|
157
|
+
end
|
158
|
+
rescue EOFError
|
159
|
+
end
|
160
|
+
|
161
|
+
if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == "ruby" && RUBY_VERSION <= "1.8")
|
162
|
+
# please note the use of IO::select() here, as it is used specifically to
|
163
|
+
# preserve correct signal handling behavior in ruby 1.8.
|
164
|
+
# https://github.com/thibaudgg/rb-fsevent/commit/d1a868bf8dc72dbca102bedbadff76c7e6c2dc21
|
165
|
+
# https://github.com/thibaudgg/rb-fsevent/blob/1ca42b987596f350ee7b19d8f8210b7b6ae8766b/ext/fsevent/fsevent_watch.c#L171
|
166
|
+
def pipe_readable?(pipe)
|
167
|
+
IO.select([pipe])
|
168
|
+
end
|
169
|
+
else
|
170
|
+
def pipe_readable?(pipe)
|
171
|
+
!pipe.eof?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def connect
|
176
|
+
Capybara.timeout(5) do
|
177
|
+
attempt_connect
|
178
|
+
!@socket.nil?
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def attempt_connect
|
183
|
+
@socket = @socket_class.open("127.0.0.1", @server_port)
|
184
|
+
rescue Errno::ECONNREFUSED
|
185
|
+
end
|
186
|
+
|
187
|
+
def check
|
188
|
+
result = @socket.gets
|
189
|
+
result.strip! if result
|
190
|
+
|
191
|
+
if result.nil?
|
192
|
+
raise WebkitNoResponseError, "No response received from the server."
|
193
|
+
elsif result != 'ok'
|
194
|
+
raise WebkitInvalidResponseError, read_response
|
195
|
+
end
|
196
|
+
|
197
|
+
result
|
198
|
+
end
|
199
|
+
|
200
|
+
def read_response
|
201
|
+
response_length = @socket.gets.to_i
|
202
|
+
response = response_length > 0 ? @socket.read(response_length) : ''
|
203
|
+
response.force_encoding("UTF-8") if response.respond_to?(:force_encoding)
|
204
|
+
response
|
205
|
+
end
|
206
|
+
|
207
|
+
def default_proxy_options
|
208
|
+
{
|
209
|
+
:host => "localhost",
|
210
|
+
:port => "0",
|
211
|
+
:user => "",
|
212
|
+
:pass => ""
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
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 do |option|
|
22
|
+
option["selected"] == "selected"
|
23
|
+
end.map do |option|
|
24
|
+
option.value
|
25
|
+
end
|
26
|
+
else
|
27
|
+
invoke "value"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def set(value)
|
32
|
+
invoke "set", value
|
33
|
+
end
|
34
|
+
|
35
|
+
def select_option
|
36
|
+
invoke "selectOption"
|
37
|
+
end
|
38
|
+
|
39
|
+
def unselect_option
|
40
|
+
select = find("ancestor::select").first
|
41
|
+
if select.multiple_select?
|
42
|
+
invoke "unselectOption"
|
43
|
+
else
|
44
|
+
raise Capybara::UnselectNotAllowed
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def click
|
49
|
+
invoke "click"
|
50
|
+
end
|
51
|
+
|
52
|
+
def drag_to(element)
|
53
|
+
invoke 'dragTo', element.native
|
54
|
+
end
|
55
|
+
|
56
|
+
def tag_name
|
57
|
+
invoke "tagName"
|
58
|
+
end
|
59
|
+
|
60
|
+
def visible?
|
61
|
+
invoke("visible") == "true"
|
62
|
+
end
|
63
|
+
|
64
|
+
def selected?
|
65
|
+
invoke("selected") == "true"
|
66
|
+
end
|
67
|
+
|
68
|
+
def checked?
|
69
|
+
self['checked']
|
70
|
+
end
|
71
|
+
|
72
|
+
def disabled?
|
73
|
+
self['disabled']
|
74
|
+
end
|
75
|
+
|
76
|
+
def path
|
77
|
+
raise Capybara::NotSupportedByDriverError
|
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
|