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.
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