intentmedia-capybara-webkit 0.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +38 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +65 -0
  7. data/LICENSE +19 -0
  8. data/README.md +67 -0
  9. data/Rakefile +78 -0
  10. data/bin/Info.plist +22 -0
  11. data/capybara-webkit.gemspec +24 -0
  12. data/extconf.rb +2 -0
  13. data/gemfiles/1.0.gemfile +7 -0
  14. data/gemfiles/1.0.gemfile.lock +65 -0
  15. data/gemfiles/1.1.gemfile +7 -0
  16. data/gemfiles/1.1.gemfile.lock +65 -0
  17. data/lib/capybara-webkit.rb +1 -0
  18. data/lib/capybara/driver/webkit.rb +113 -0
  19. data/lib/capybara/driver/webkit/browser.rb +216 -0
  20. data/lib/capybara/driver/webkit/node.rb +118 -0
  21. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  22. data/lib/capybara/webkit.rb +11 -0
  23. data/lib/capybara_webkit_builder.rb +40 -0
  24. data/spec/browser_spec.rb +178 -0
  25. data/spec/driver_rendering_spec.rb +80 -0
  26. data/spec/driver_spec.rb +1048 -0
  27. data/spec/integration/driver_spec.rb +20 -0
  28. data/spec/integration/session_spec.rb +137 -0
  29. data/spec/self_signed_ssl_cert.rb +42 -0
  30. data/spec/spec_helper.rb +25 -0
  31. data/src/Body.h +12 -0
  32. data/src/ClearCookies.cpp +18 -0
  33. data/src/ClearCookies.h +11 -0
  34. data/src/Command.cpp +15 -0
  35. data/src/Command.h +29 -0
  36. data/src/CommandFactory.cpp +29 -0
  37. data/src/CommandFactory.h +16 -0
  38. data/src/CommandParser.cpp +68 -0
  39. data/src/CommandParser.h +29 -0
  40. data/src/Connection.cpp +82 -0
  41. data/src/Connection.h +36 -0
  42. data/src/Evaluate.cpp +84 -0
  43. data/src/Evaluate.h +22 -0
  44. data/src/Execute.cpp +16 -0
  45. data/src/Execute.h +12 -0
  46. data/src/Find.cpp +19 -0
  47. data/src/Find.h +13 -0
  48. data/src/FrameFocus.cpp +66 -0
  49. data/src/FrameFocus.h +28 -0
  50. data/src/GetCookies.cpp +22 -0
  51. data/src/GetCookies.h +14 -0
  52. data/src/Header.cpp +18 -0
  53. data/src/Header.h +11 -0
  54. data/src/Headers.cpp +11 -0
  55. data/src/Headers.h +12 -0
  56. data/src/JavascriptInvocation.cpp +14 -0
  57. data/src/JavascriptInvocation.h +19 -0
  58. data/src/NetworkAccessManager.cpp +25 -0
  59. data/src/NetworkAccessManager.h +18 -0
  60. data/src/NetworkCookieJar.cpp +101 -0
  61. data/src/NetworkCookieJar.h +15 -0
  62. data/src/Node.cpp +14 -0
  63. data/src/Node.h +13 -0
  64. data/src/Render.cpp +19 -0
  65. data/src/Render.h +12 -0
  66. data/src/Reset.cpp +20 -0
  67. data/src/Reset.h +12 -0
  68. data/src/Response.cpp +19 -0
  69. data/src/Response.h +13 -0
  70. data/src/Server.cpp +25 -0
  71. data/src/Server.h +21 -0
  72. data/src/SetCookie.cpp +18 -0
  73. data/src/SetCookie.h +11 -0
  74. data/src/SetProxy.cpp +24 -0
  75. data/src/SetProxy.h +11 -0
  76. data/src/Source.cpp +20 -0
  77. data/src/Source.h +19 -0
  78. data/src/Status.cpp +13 -0
  79. data/src/Status.h +12 -0
  80. data/src/UnsupportedContentHandler.cpp +32 -0
  81. data/src/UnsupportedContentHandler.h +18 -0
  82. data/src/Url.cpp +15 -0
  83. data/src/Url.h +12 -0
  84. data/src/Visit.cpp +21 -0
  85. data/src/Visit.h +15 -0
  86. data/src/WebPage.cpp +226 -0
  87. data/src/WebPage.h +54 -0
  88. data/src/body.cpp +11 -0
  89. data/src/capybara.js +205 -0
  90. data/src/find_command.h +24 -0
  91. data/src/main.cpp +34 -0
  92. data/src/webkit_server.pro +71 -0
  93. data/src/webkit_server.qrc +5 -0
  94. data/templates/Command.cpp +10 -0
  95. data/templates/Command.h +12 -0
  96. data/webkit_server.pro +4 -0
  97. metadata +246 -0
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "capybara", "~> 1.1.0"
6
+
7
+ gemspec :path=>"../"
@@ -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