capybara-webkit 1.1.1 → 1.2.0

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +19 -13
  4. data/Appraisals +8 -4
  5. data/Gemfile.lock +29 -14
  6. data/NEWS.md +4 -0
  7. data/README.md +1 -1
  8. data/Rakefile +1 -1
  9. data/capybara-webkit.gemspec +2 -1
  10. data/gemfiles/2.0.gemfile.lock +10 -6
  11. data/gemfiles/2.1.gemfile.lock +22 -9
  12. data/gemfiles/2.2.gemfile +7 -0
  13. data/gemfiles/2.2.gemfile.lock +77 -0
  14. data/gemfiles/2.3.gemfile +7 -0
  15. data/gemfiles/2.3.gemfile.lock +77 -0
  16. data/lib/capybara/webkit/browser.rb +28 -4
  17. data/lib/capybara/webkit/connection.rb +9 -24
  18. data/lib/capybara/webkit/driver.rb +40 -4
  19. data/lib/capybara/webkit/errors.rb +3 -0
  20. data/lib/capybara/webkit/version.rb +1 -1
  21. data/spec/browser_spec.rb +1 -1
  22. data/spec/connection_spec.rb +27 -5
  23. data/spec/driver_rendering_spec.rb +10 -0
  24. data/spec/driver_resize_window_spec.rb +22 -0
  25. data/spec/driver_spec.rb +117 -9
  26. data/spec/selenium_compatibility_spec.rb +3 -0
  27. data/spec/spec_helper.rb +22 -3
  28. data/spec/support/matchers/include_response.rb +24 -0
  29. data/src/CommandFactory.cpp +7 -1
  30. data/src/GoBack.cpp +12 -0
  31. data/src/GoBack.h +10 -0
  32. data/src/GoForward.cpp +12 -0
  33. data/src/GoForward.h +10 -0
  34. data/src/Render.cpp +10 -1
  35. data/src/Reset.cpp +0 -2
  36. data/src/SocketCommand.cpp +6 -0
  37. data/src/SocketCommand.h +1 -0
  38. data/src/StdinNotifier.cpp +16 -0
  39. data/src/StdinNotifier.h +20 -0
  40. data/src/WebPage.cpp +20 -5
  41. data/src/WebPage.h +4 -1
  42. data/src/WebPageManager.cpp +18 -7
  43. data/src/WebPageManager.h +2 -1
  44. data/src/WindowClose.cpp +10 -0
  45. data/src/WindowClose.h +12 -0
  46. data/src/WindowCommand.cpp +27 -0
  47. data/src/WindowCommand.h +21 -0
  48. data/src/WindowFocus.cpp +2 -25
  49. data/src/WindowFocus.h +4 -7
  50. data/src/WindowMaximize.cpp +14 -0
  51. data/src/WindowMaximize.h +12 -0
  52. data/src/WindowOpen.cpp +12 -0
  53. data/src/WindowOpen.h +10 -0
  54. data/src/WindowResize.cpp +16 -0
  55. data/src/WindowResize.h +12 -0
  56. data/src/WindowSize.cpp +17 -0
  57. data/src/WindowSize.h +12 -0
  58. data/src/capybara.js +19 -3
  59. data/src/find_command.h +7 -1
  60. data/src/main.cpp +4 -0
  61. data/src/webkit_server.pro +20 -4
  62. data/templates/Command.cpp +2 -0
  63. data/templates/Command.h +1 -3
  64. metadata +82 -54
  65. data/src/ResizeWindow.cpp +0 -17
  66. data/src/ResizeWindow.h +0 -10
@@ -102,6 +102,26 @@ module Capybara::Webkit
102
102
  command("WindowFocus", selector)
103
103
  end
104
104
 
105
+ def window_open
106
+ command("WindowOpen")
107
+ end
108
+
109
+ def window_close(selector)
110
+ command("WindowClose", selector)
111
+ end
112
+
113
+ def window_resize(handle, width, height)
114
+ command("WindowResize", handle, width.to_i, height.to_i)
115
+ end
116
+
117
+ def window_size(handle)
118
+ JSON.parse(command("WindowSize", handle))
119
+ end
120
+
121
+ def window_maximize(handle)
122
+ command("WindowMaximize", handle)
123
+ end
124
+
105
125
  def get_window_handles
106
126
  JSON.parse(command('GetWindowHandles'))
107
127
  end
@@ -195,14 +215,18 @@ module Capybara::Webkit
195
215
  command("SetProxy")
196
216
  end
197
217
 
198
- def resize_window(width, height)
199
- command("ResizeWindow", width.to_i, height.to_i)
200
- end
201
-
202
218
  def version
203
219
  command("Version")
204
220
  end
205
221
 
222
+ def go_back
223
+ command("GoBack")
224
+ end
225
+
226
+ def go_forward
227
+ command("GoForward")
228
+ end
229
+
206
230
  private
207
231
 
208
232
  def check
@@ -8,7 +8,7 @@ module Capybara::Webkit
8
8
  SERVER_PATH = File.expand_path("../../../../bin/webkit_server", __FILE__)
9
9
  WEBKIT_SERVER_START_TIMEOUT = 15
10
10
 
11
- attr_reader :port
11
+ attr_reader :port, :pid
12
12
 
13
13
  def initialize(options = {})
14
14
  @socket = nil
@@ -16,7 +16,8 @@ module Capybara::Webkit
16
16
  if options.has_key?(:stderr)
17
17
  @output_target = options[:stderr]
18
18
  elsif options.has_key?(:stdout)
19
- warn "[DEPRECATION] The `stdout` option is deprecated. Please use `stderr` instead."
19
+ warn '[DEPRECATION] The Capybara::Webkit::Connection `stdout` option ' \
20
+ 'is deprecated. Please use `stderr` instead.'
20
21
  @output_target = options[:stdout]
21
22
  else
22
23
  @output_target = $stderr
@@ -46,32 +47,12 @@ module Capybara::Webkit
46
47
  def start_server
47
48
  open_pipe
48
49
  discover_port
50
+ discover_pid
49
51
  forward_output_in_background_thread
50
52
  end
51
53
 
52
54
  def open_pipe
53
- _, @pipe_stdout, @pipe_stderr, wait_thr = Open3.popen3(SERVER_PATH)
54
- @pid = wait_thr[:pid]
55
- register_shutdown_hook
56
- end
57
-
58
- def register_shutdown_hook
59
- @owner_pid = Process.pid
60
- at_exit do
61
- if Process.pid == @owner_pid
62
- kill_process
63
- end
64
- end
65
- end
66
-
67
- def kill_process
68
- if RUBY_PLATFORM =~ /mingw32/
69
- Process.kill(9, @pid)
70
- else
71
- Process.kill("INT", @pid)
72
- end
73
- rescue Errno::ESRCH
74
- # This just means that the webkit_server process has already ended
55
+ @pipe_stdin, @pipe_stdout, @pipe_stderr, @wait_thr = Open3.popen3(SERVER_PATH)
75
56
  end
76
57
 
77
58
  def discover_port
@@ -80,6 +61,10 @@ module Capybara::Webkit
80
61
  end
81
62
  end
82
63
 
64
+ def discover_pid
65
+ @pid = @wait_thr[:pid]
66
+ end
67
+
83
68
  def forward_output_in_background_thread
84
69
  Thread.new do
85
70
  Thread.current.abort_on_exception = true
@@ -89,7 +89,15 @@ module Capybara::Webkit
89
89
  end
90
90
 
91
91
  def resize_window(width, height)
92
- browser.resize_window(width, height)
92
+ resize_window_to(current_window_handle, width, height)
93
+ end
94
+
95
+ def resize_window_to(handle, width, height)
96
+ browser.window_resize(handle, width, height)
97
+ end
98
+
99
+ def window_size(handle)
100
+ browser.window_size(handle)
93
101
  end
94
102
 
95
103
  def within_frame(selector)
@@ -102,8 +110,8 @@ module Capybara::Webkit
102
110
  end
103
111
 
104
112
  def within_window(selector)
105
- current_window = window_handle
106
- browser.window_focus(selector)
113
+ current_window = current_window_handle
114
+ switch_to_window(selector)
107
115
  begin
108
116
  yield
109
117
  ensure
@@ -111,14 +119,30 @@ module Capybara::Webkit
111
119
  end
112
120
  end
113
121
 
122
+ def switch_to_window(selector)
123
+ browser.window_focus(selector)
124
+ end
125
+
114
126
  def window_handles
115
127
  browser.get_window_handles
116
128
  end
117
129
 
118
- def window_handle
130
+ def current_window_handle
119
131
  browser.get_window_handle
120
132
  end
121
133
 
134
+ def open_new_window
135
+ browser.window_open
136
+ end
137
+
138
+ def close_window(selector)
139
+ browser.window_close(selector)
140
+ end
141
+
142
+ def maximize_window(selector)
143
+ browser.window_maximize(selector)
144
+ end
145
+
122
146
  def accept_js_confirms!
123
147
  browser.accept_js_confirms
124
148
  end
@@ -143,6 +167,14 @@ module Capybara::Webkit
143
167
  end
144
168
  end
145
169
 
170
+ def go_back
171
+ browser.go_back
172
+ end
173
+
174
+ def go_forward
175
+ browser.go_forward
176
+ end
177
+
146
178
  def wait?
147
179
  true
148
180
  end
@@ -174,6 +206,10 @@ module Capybara::Webkit
174
206
  [Capybara::Webkit::ClickFailed]
175
207
  end
176
208
 
209
+ def no_such_window_error
210
+ Capybara::Webkit::NoSuchWindowError
211
+ end
212
+
177
213
  def version
178
214
  [
179
215
  "Capybara: #{Capybara::VERSION}",
@@ -14,6 +14,9 @@ module Capybara::Webkit
14
14
  class TimeoutError < Timeout::Error
15
15
  end
16
16
 
17
+ class NoSuchWindowError < StandardError
18
+ end
19
+
17
20
  class JsonError
18
21
  def initialize(response)
19
22
  error = JSON.parse response
@@ -1,7 +1,7 @@
1
1
  module Capybara
2
2
  module Driver
3
3
  class Webkit
4
- VERSION = '1.1.1'.freeze
4
+ VERSION = '1.2.0'.freeze
5
5
  end
6
6
  end
7
7
  end
data/spec/browser_spec.rb CHANGED
@@ -148,7 +148,7 @@ describe Capybara::Webkit::Browser do
148
148
  end
149
149
  end
150
150
 
151
- describe "forking", :skip_on_windows => true do
151
+ describe "forking", skip_on_windows: true, skip_on_jruby: true do
152
152
  it "only shuts down the server from the main process" do
153
153
  browser.reset!
154
154
  pid = fork {}
@@ -2,6 +2,27 @@ require 'spec_helper'
2
2
  require 'capybara/webkit/connection'
3
3
 
4
4
  describe Capybara::Webkit::Connection do
5
+ it "kills the process when the parent process dies", skip_on_windows: true, skip_on_jruby: true do
6
+ read_io, write_io = IO.pipe
7
+
8
+ fork_pid = fork do
9
+ read_io.close
10
+ connection = Capybara::Webkit::Connection.new
11
+ write_io.write(connection.pid)
12
+ write_io.close
13
+ Process.wait(connection.pid)
14
+ end
15
+
16
+ write_io.close
17
+
18
+ webkit_pid = read_io.read.to_i
19
+ webkit_pid.should be > 1
20
+ read_io.close
21
+ Process.kill(9, fork_pid)
22
+ sleep 1
23
+ expect { Process.getpgid(webkit_pid) }.to raise_error Errno::ESRCH
24
+ end
25
+
5
26
  it "boots a server to talk to" do
6
27
  url = "http://#{@rack_server.host}:#{@rack_server.port}/"
7
28
  connection.puts "Visit"
@@ -19,17 +40,18 @@ describe Capybara::Webkit::Connection do
19
40
  end
20
41
 
21
42
  it 'forwards stderr to the given IO object' do
22
- io = StringIO.new
23
- redirected_connection = Capybara::Webkit::Connection.new(:stderr => io)
24
- script = 'console.log("hello world")'
43
+ read_io, write_io = IO.pipe
44
+ redirected_connection = Capybara::Webkit::Connection.new(:stderr => write_io)
25
45
  redirected_connection.puts "EnableLogging"
26
46
  redirected_connection.puts 0
47
+
48
+ script = 'console.log("hello world")'
27
49
  redirected_connection.puts "Execute"
28
50
  redirected_connection.puts 1
29
51
  redirected_connection.puts script.to_s.bytesize
30
52
  redirected_connection.print script
31
- sleep(0.5)
32
- io.string.should =~ /hello world $/
53
+
54
+ expect(read_io).to include_response "\nhello world"
33
55
  end
34
56
 
35
57
  it 'does not forward stderr to nil' do
@@ -90,4 +90,14 @@ describe Capybara::Webkit::Driver, "rendering an image" do
90
90
  driver.evaluate_script('window.innerHeight').should eq 600
91
91
  end
92
92
  end
93
+
94
+ context "with invalid filepath" do
95
+ before do
96
+ @file_name = File.dirname(@file_name)
97
+ end
98
+
99
+ it "raises an InvalidResponseError" do
100
+ expect { render({}) }.to raise_error(Capybara::Webkit::InvalidResponseError)
101
+ end
102
+ end
93
103
  end
@@ -45,4 +45,26 @@ describe Capybara::Webkit::Driver, "#resize_window(width, height)" do
45
45
  driver.visit("#{AppRunner.app_host}/")
46
46
  driver.html.should include(DEFAULT_DIMENTIONS)
47
47
  end
48
+
49
+ it "resizes windows by handle" do
50
+ driver.visit("#{AppRunner.app_host}/")
51
+ driver.open_new_window
52
+ driver.visit("#{AppRunner.app_host}/")
53
+
54
+ driver.resize_window_to(driver.window_handles.first, 800, 600)
55
+ driver.resize_window_to(driver.window_handles.last, 400, 300)
56
+
57
+ driver.window_size(driver.window_handles.first).should eq [800, 600]
58
+ driver.window_size(driver.window_handles.last).should eq [400, 300]
59
+ end
60
+
61
+ it "maximizes a window" do
62
+ driver.visit("#{AppRunner.app_host}/")
63
+ driver.resize_window(400, 300)
64
+ driver.maximize_window(driver.current_window_handle)
65
+ width, height = *driver.window_size(driver.current_window_handle)
66
+
67
+ width.should be > 400
68
+ height.should be > 300
69
+ end
48
70
  end
data/spec/driver_spec.rb CHANGED
@@ -341,6 +341,9 @@ describe Capybara::Webkit::Driver do
341
341
  <div id="hidden-text">
342
342
  Some of this text is <em style="display:none">hidden!</em>
343
343
  </div>
344
+ <div id="hidden-ancestor" style="display: none">
345
+ <div>Hello</div>
346
+ </div>
344
347
  <input type="text" disabled="disabled"/>
345
348
  <input id="checktest" type="checkbox" checked="checked"/>
346
349
  <script type="text/javascript">
@@ -353,6 +356,12 @@ describe Capybara::Webkit::Driver do
353
356
 
354
357
  before { visit("/") }
355
358
 
359
+ it "doesn't return text if the ancestor is hidden" do
360
+ visit("/")
361
+
362
+ driver.find_css("#hidden-ancestor div").first.text.should eq ''
363
+ end
364
+
356
365
  it "handles anchor tags" do
357
366
  visit("#test")
358
367
  driver.find_xpath("//*[contains(., 'hello')]").should_not be_empty
@@ -1081,7 +1090,7 @@ describe Capybara::Webkit::Driver do
1081
1090
  <input class="watch" type="password"/>
1082
1091
  <input class="watch" type="search"/>
1083
1092
  <input class="watch" type="tel"/>
1084
- <input class="watch" type="text"/>
1093
+ <input class="watch" type="text" value="original"/>
1085
1094
  <input class="watch" type="url"/>
1086
1095
  <textarea class="watch"></textarea>
1087
1096
  <input class="watch" type="checkbox"/>
@@ -1117,7 +1126,7 @@ describe Capybara::Webkit::Driver do
1117
1126
 
1118
1127
  before { visit("/") }
1119
1128
 
1120
- let(:newtext) { 'newvalue' }
1129
+ let(:newtext) { '12345' }
1121
1130
 
1122
1131
  let(:keyevents) do
1123
1132
  (%w{focus} +
@@ -1125,13 +1134,22 @@ describe Capybara::Webkit::Driver do
1125
1134
  ).flatten
1126
1135
  end
1127
1136
 
1137
+ let(:textevents) { keyevents + %w(change blur) }
1138
+
1128
1139
  %w(email number password search tel text url).each do | field_type |
1129
1140
  it "triggers text input events on inputs of type #{field_type}" do
1130
1141
  driver.find_xpath("//input[@type='#{field_type}']").first.set(newtext)
1131
- driver.find_xpath("//li").map(&:visible_text).should eq keyevents
1142
+ driver.find_xpath("//body").first.click
1143
+ driver.find_xpath("//li").map(&:visible_text).should eq textevents
1132
1144
  end
1133
1145
  end
1134
1146
 
1147
+ it "triggers events for cleared inputs" do
1148
+ driver.find_xpath("//input[@type='text']").first.set('')
1149
+ driver.find_xpath("//body").first.click
1150
+ driver.find_xpath("//li").map(&:visible_text).should include('change')
1151
+ end
1152
+
1135
1153
  it "triggers textarea input events" do
1136
1154
  driver.find_xpath("//textarea").first.set(newtext)
1137
1155
  driver.find_xpath("//li").map(&:visible_text).should eq keyevents
@@ -1925,6 +1943,42 @@ describe Capybara::Webkit::Driver do
1925
1943
  end
1926
1944
  end
1927
1945
 
1946
+ it "can switch to another window" do
1947
+ visit("/new_window")
1948
+ driver.switch_to_window(driver.window_handles.last)
1949
+ driver.find_xpath("//p").first.visible_text.should eq "finished"
1950
+ end
1951
+
1952
+ it "knows the current window handle" do
1953
+ visit("/new_window")
1954
+ driver.within_window(driver.window_handles.last) do
1955
+ driver.current_window_handle.should eq driver.window_handles.last
1956
+ end
1957
+ end
1958
+
1959
+ it "can close the current window" do
1960
+ visit("/new_window")
1961
+ original_handle = driver.current_window_handle
1962
+ driver.switch_to_window(driver.window_handles.last)
1963
+ driver.close_window(driver.current_window_handle)
1964
+
1965
+ driver.current_window_handle.should eq(original_handle)
1966
+ end
1967
+
1968
+ it "can close an unfocused window" do
1969
+ visit("/new_window")
1970
+ driver.close_window(driver.window_handles.last)
1971
+ driver.window_handles.size.should eq(1)
1972
+ end
1973
+
1974
+ it "can close the last window" do
1975
+ visit("/new_window")
1976
+ handles = driver.window_handles
1977
+ handles.each { |handle| driver.close_window(handle) }
1978
+ driver.html.should be_empty
1979
+ handles.should_not include(driver.current_window_handle)
1980
+ end
1981
+
1928
1982
  it "waits for the new window to load" do
1929
1983
  visit("/new_window?sleep=1")
1930
1984
  driver.within_window(driver.window_handles.last) do
@@ -1969,7 +2023,7 @@ describe Capybara::Webkit::Driver do
1969
2023
 
1970
2024
  it "raises an error if the window is not found" do
1971
2025
  expect { driver.within_window('myWindowDoesNotExist') }.
1972
- to raise_error(Capybara::Webkit::InvalidResponseError)
2026
+ to raise_error(Capybara::Webkit::NoSuchWindowError)
1973
2027
  end
1974
2028
 
1975
2029
  it "has a number of window handles equal to the number of open windows" do
@@ -1978,12 +2032,35 @@ describe Capybara::Webkit::Driver do
1978
2032
  driver.window_handles.size.should eq 2
1979
2033
  end
1980
2034
 
2035
+ it "removes windows when closed via JavaScript" do
2036
+ visit("/new_window")
2037
+ driver.execute_script('console.log(window.document.title); window.close()')
2038
+ sleep 2
2039
+ driver.window_handles.size.should eq 1
2040
+ end
2041
+
1981
2042
  it "closes new windows on reset" do
1982
2043
  visit("/new_window")
1983
2044
  last_handle = driver.window_handles.last
1984
2045
  driver.reset!
1985
2046
  driver.window_handles.should_not include(last_handle)
1986
2047
  end
2048
+
2049
+ it "leaves the old window focused when opening a new window" do
2050
+ visit("/new_window")
2051
+ current_window = driver.current_window_handle
2052
+ driver.open_new_window
2053
+
2054
+ driver.current_window_handle.should eq current_window
2055
+ driver.window_handles.size.should eq 3
2056
+ end
2057
+
2058
+ it "opens blank windows" do
2059
+ visit("/new_window")
2060
+ driver.open_new_window
2061
+ driver.switch_to_window(driver.window_handles.last)
2062
+ driver.html.should be_empty
2063
+ end
1987
2064
  end
1988
2065
 
1989
2066
  it "preserves cookies across windows" do
@@ -2246,28 +2323,31 @@ describe Capybara::Webkit::Driver do
2246
2323
  describe "logger app" do
2247
2324
  it "logs nothing before turning on the logger" do
2248
2325
  visit("/")
2326
+ @write.close
2249
2327
  log.should_not include logging_message
2250
2328
  end
2251
2329
 
2252
2330
  it "logs its commands after turning on the logger" do
2253
2331
  driver.enable_logging
2254
2332
  visit("/")
2333
+ @write.close
2255
2334
  log.should include logging_message
2256
2335
  end
2257
2336
 
2337
+ before do
2338
+ @read, @write = IO.pipe
2339
+ end
2340
+
2258
2341
  let(:logging_message) { 'Wrote response true' }
2259
2342
 
2260
2343
  let(:driver) do
2261
- connection = Capybara::Webkit::Connection.new(:stderr => output)
2344
+ connection = Capybara::Webkit::Connection.new(:stderr => @write)
2262
2345
  browser = Capybara::Webkit::Browser.new(connection)
2263
2346
  Capybara::Webkit::Driver.new(AppRunner.app, :browser => browser)
2264
2347
  end
2265
2348
 
2266
- let(:output) { StringIO.new }
2267
-
2268
2349
  def log
2269
- output.rewind
2270
- output.read
2350
+ @read.read
2271
2351
  end
2272
2352
  end
2273
2353
 
@@ -2383,6 +2463,34 @@ describe Capybara::Webkit::Driver do
2383
2463
  end
2384
2464
  end
2385
2465
 
2466
+ context "history" do
2467
+ let(:driver) do
2468
+ driver_for_app do
2469
+ get "/:param" do |param|
2470
+ <<-HTML
2471
+ <html>
2472
+ <body>
2473
+ <p>#{param}</p>
2474
+ <a href="/navigated">Navigate</a>
2475
+ </body>
2476
+ </html>
2477
+ HTML
2478
+ end
2479
+ end
2480
+ end
2481
+
2482
+ it "can navigate in history" do
2483
+ visit("/first")
2484
+ driver.find_xpath("//p").first.text.should eq('first')
2485
+ driver.find_xpath("//a").first.click
2486
+ driver.find_xpath("//p").first.text.should eq('navigated')
2487
+ driver.go_back
2488
+ driver.find_xpath("//p").first.text.should eq('first')
2489
+ driver.go_forward
2490
+ driver.find_xpath("//p").first.text.should eq('navigated')
2491
+ end
2492
+ end
2493
+
2386
2494
  def driver_url(driver, path)
2387
2495
  URI.parse(driver.current_url).merge(path).to_s
2388
2496
  end