capybara-webkit 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +2 -1
  2. data/GOALS +9 -0
  3. data/Gemfile.lock +15 -12
  4. data/LICENSE +1 -1
  5. data/NEWS.md +18 -0
  6. data/README.md +81 -2
  7. data/capybara-webkit.gemspec +1 -1
  8. data/lib/capybara/webkit.rb +11 -6
  9. data/lib/capybara/{driver/webkit → webkit}/browser.rb +90 -4
  10. data/lib/capybara/{driver/webkit → webkit}/connection.rb +48 -47
  11. data/lib/capybara/{driver/webkit → webkit}/cookie_jar.rb +1 -1
  12. data/lib/capybara/webkit/driver.rb +186 -0
  13. data/lib/capybara/webkit/errors.rb +10 -0
  14. data/lib/capybara/{driver/webkit → webkit}/node.rb +12 -4
  15. data/lib/capybara/{driver/webkit → webkit}/socket_debugger.rb +4 -1
  16. data/lib/capybara/{driver/webkit → webkit}/version.rb +1 -1
  17. data/lib/capybara_webkit_builder.rb +17 -3
  18. data/spec/browser_spec.rb +95 -7
  19. data/spec/connection_spec.rb +16 -5
  20. data/spec/cookie_jar_spec.rb +3 -3
  21. data/spec/driver_rendering_spec.rb +19 -26
  22. data/spec/driver_resize_window_spec.rb +3 -3
  23. data/spec/driver_spec.rb +1200 -822
  24. data/spec/integration/driver_spec.rb +4 -3
  25. data/spec/selenium_compatibility_spec.rb +49 -0
  26. data/spec/spec_helper.rb +14 -6
  27. data/spec/support/app_runner.rb +94 -0
  28. data/src/Authenticate.cpp +18 -0
  29. data/src/Authenticate.h +12 -0
  30. data/src/Body.h +3 -5
  31. data/src/ClearCookies.cpp +3 -5
  32. data/src/ClearCookies.h +3 -5
  33. data/src/ClearPromptText.cpp +11 -0
  34. data/src/ClearPromptText.h +9 -0
  35. data/src/Command.cpp +4 -15
  36. data/src/Command.h +3 -14
  37. data/src/CommandFactory.cpp +20 -6
  38. data/src/CommandFactory.h +3 -2
  39. data/src/CommandParser.cpp +1 -1
  40. data/src/Connection.cpp +22 -21
  41. data/src/Connection.h +5 -6
  42. data/src/ConsoleMessages.cpp +2 -1
  43. data/src/ConsoleMessages.h +3 -5
  44. data/src/CurrentUrl.cpp +9 -48
  45. data/src/CurrentUrl.h +8 -8
  46. data/src/EnableLogging.cpp +10 -0
  47. data/src/EnableLogging.h +12 -0
  48. data/src/Evaluate.cpp +2 -1
  49. data/src/Evaluate.h +3 -5
  50. data/src/Execute.cpp +2 -1
  51. data/src/Execute.h +3 -5
  52. data/src/Find.cpp +3 -2
  53. data/src/Find.h +3 -5
  54. data/src/FrameFocus.cpp +3 -2
  55. data/src/FrameFocus.h +3 -4
  56. data/src/GetCookies.cpp +3 -4
  57. data/src/GetCookies.h +3 -5
  58. data/src/GetTimeout.cpp +9 -0
  59. data/src/GetTimeout.h +11 -0
  60. data/src/GetWindowHandle.cpp +11 -0
  61. data/src/GetWindowHandle.h +10 -0
  62. data/src/GetWindowHandles.cpp +20 -0
  63. data/src/GetWindowHandles.h +10 -0
  64. data/src/Header.cpp +2 -1
  65. data/src/Header.h +3 -5
  66. data/src/Headers.cpp +8 -2
  67. data/src/Headers.h +3 -5
  68. data/src/IgnoreSslErrors.cpp +4 -3
  69. data/src/IgnoreSslErrors.h +3 -5
  70. data/src/JavascriptAlertMessages.cpp +10 -0
  71. data/src/JavascriptAlertMessages.h +9 -0
  72. data/src/JavascriptConfirmMessages.cpp +10 -0
  73. data/src/JavascriptConfirmMessages.h +9 -0
  74. data/src/JavascriptInvocation.cpp +1 -1
  75. data/src/JavascriptInvocation.h +1 -1
  76. data/src/JavascriptPromptMessages.cpp +10 -0
  77. data/src/JavascriptPromptMessages.h +9 -0
  78. data/src/NetworkAccessManager.cpp +38 -5
  79. data/src/NetworkAccessManager.h +20 -0
  80. data/src/Node.cpp +6 -1
  81. data/src/Node.h +4 -5
  82. data/src/NullCommand.cpp +5 -2
  83. data/src/NullCommand.h +4 -3
  84. data/src/PageLoadingCommand.cpp +12 -7
  85. data/src/PageLoadingCommand.h +6 -9
  86. data/src/Render.cpp +2 -1
  87. data/src/Render.h +3 -5
  88. data/src/RequestedUrl.cpp +2 -1
  89. data/src/RequestedUrl.h +3 -5
  90. data/src/Reset.cpp +3 -17
  91. data/src/Reset.h +3 -8
  92. data/src/ResizeWindow.cpp +2 -1
  93. data/src/ResizeWindow.h +3 -5
  94. data/src/Response.cpp +4 -0
  95. data/src/Response.h +1 -0
  96. data/src/Server.cpp +2 -3
  97. data/src/Server.h +0 -2
  98. data/src/SetConfirmAction.cpp +11 -0
  99. data/src/SetConfirmAction.h +9 -0
  100. data/src/SetCookie.cpp +3 -4
  101. data/src/SetCookie.h +3 -5
  102. data/src/SetPromptAction.cpp +11 -0
  103. data/src/SetPromptAction.h +9 -0
  104. data/src/SetPromptText.cpp +11 -0
  105. data/src/SetPromptText.h +9 -0
  106. data/src/SetProxy.cpp +2 -1
  107. data/src/SetProxy.h +3 -5
  108. data/src/SetSkipImageLoading.cpp +12 -0
  109. data/src/SetSkipImageLoading.h +9 -0
  110. data/src/SetTimeout.cpp +19 -0
  111. data/src/SetTimeout.h +9 -0
  112. data/src/SocketCommand.cpp +21 -0
  113. data/src/SocketCommand.h +29 -0
  114. data/src/Source.cpp +3 -2
  115. data/src/Source.h +3 -4
  116. data/src/Status.cpp +2 -1
  117. data/src/Status.h +3 -5
  118. data/src/TimeoutCommand.cpp +69 -0
  119. data/src/TimeoutCommand.h +41 -0
  120. data/src/UnsupportedContentHandler.cpp +11 -17
  121. data/src/UnsupportedContentHandler.h +5 -3
  122. data/src/Url.cpp +2 -1
  123. data/src/Url.h +3 -5
  124. data/src/Visit.cpp +3 -2
  125. data/src/Visit.h +3 -5
  126. data/src/WebPage.cpp +129 -44
  127. data/src/WebPage.h +37 -11
  128. data/src/WebPageManager.cpp +127 -0
  129. data/src/WebPageManager.h +59 -0
  130. data/src/WindowFocus.cpp +32 -0
  131. data/src/WindowFocus.h +15 -0
  132. data/src/body.cpp +2 -1
  133. data/src/capybara.js +38 -10
  134. data/src/find_command.h +17 -2
  135. data/src/main.cpp +0 -2
  136. data/src/webkit_server.pro +36 -0
  137. data/templates/Command.cpp +2 -4
  138. data/templates/Command.h +3 -3
  139. metadata +106 -27
  140. data/ChangeLog +0 -70
  141. data/lib/capybara/driver/webkit.rb +0 -136
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'capybara/driver/webkit/connection'
2
+ require 'capybara/webkit/connection'
3
3
 
4
- describe Capybara::Driver::Webkit::Connection do
4
+ describe Capybara::Webkit::Connection do
5
5
  it "boots a server to talk to" do
6
6
  url = @rack_server.url("/")
7
7
  connection.puts "Visit"
@@ -20,7 +20,7 @@ describe Capybara::Driver::Webkit::Connection do
20
20
 
21
21
  it 'forwards stdout to the given IO object' do
22
22
  io = StringIO.new
23
- redirected_connection = Capybara::Driver::Webkit::Connection.new(:stdout => io)
23
+ redirected_connection = Capybara::Webkit::Connection.new(:stdout => io)
24
24
  script = 'console.log("hello world")'
25
25
  redirected_connection.puts "Execute"
26
26
  redirected_connection.puts 1
@@ -34,12 +34,23 @@ describe Capybara::Driver::Webkit::Connection do
34
34
  connection.port.should be_between 0x400, 0xffff
35
35
  end
36
36
 
37
+ it 'sets appropriate options on its socket' do
38
+ socket = stub('socket')
39
+ TCPSocket.stub(:open).and_return(socket)
40
+ if defined?(Socket::TCP_NODELAY)
41
+ socket.should_receive(:setsockopt).with(:IPPROTO_TCP, :TCP_NODELAY, 1)
42
+ else
43
+ socket.should_not_receive(:setsockopt)
44
+ end
45
+ Capybara::Webkit::Connection.new
46
+ end
47
+
37
48
  it "chooses a new port number for a new connection" do
38
- new_connection = Capybara::Driver::Webkit::Connection.new
49
+ new_connection = Capybara::Webkit::Connection.new
39
50
  new_connection.port.should_not == connection.port
40
51
  end
41
52
 
42
- let(:connection) { Capybara::Driver::Webkit::Connection.new }
53
+ let(:connection) { Capybara::Webkit::Connection.new }
43
54
 
44
55
  before(:all) do
45
56
  @app = lambda do |env|
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'capybara/driver/webkit/cookie_jar'
2
+ require 'capybara/webkit/cookie_jar'
3
3
 
4
- describe Capybara::Driver::Webkit::CookieJar do
4
+ describe Capybara::Webkit::CookieJar do
5
5
  let(:browser) {
6
6
  browser = double("Browser")
7
7
  browser.stub(:get_cookies) { [
@@ -13,7 +13,7 @@ describe Capybara::Driver::Webkit::CookieJar do
13
13
  browser
14
14
  }
15
15
 
16
- subject { Capybara::Driver::Webkit::CookieJar.new(browser) }
16
+ subject { Capybara::Webkit::CookieJar.new(browser) }
17
17
 
18
18
  describe "#find" do
19
19
  it "returns a cookie object" do
@@ -1,43 +1,37 @@
1
1
  require 'spec_helper'
2
- require 'capybara/driver/webkit'
2
+ require 'capybara/webkit/driver'
3
3
  require 'mini_magick'
4
4
 
5
- describe Capybara::Driver::Webkit, "rendering an image" do
5
+ describe Capybara::Webkit::Driver, "rendering an image" do
6
+ include AppRunner
7
+
8
+ let(:driver) do
9
+ driver_for_html(<<-HTML)
10
+ <html>
11
+ <body>
12
+ <h1>Hello World</h1>
13
+ </body>
14
+ </html>
15
+ HTML
16
+ end
6
17
 
7
- before(:all) do
18
+ before(:each) do
8
19
  # Set up the tmp directory and file name
9
20
  tmp_dir = File.join(PROJECT_ROOT, 'tmp')
10
21
  FileUtils.mkdir_p tmp_dir
11
22
  @file_name = File.join(tmp_dir, 'render-test.png')
12
-
13
- app = lambda do |env|
14
- body = <<-HTML
15
- <html>
16
- <body>
17
- <h1>Hello World</h1>
18
- </body>
19
- </html>
20
- HTML
21
- [200,
22
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
23
- [body]]
24
- end
25
-
26
- @driver = Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
27
- @driver.visit("/hello/world?success=true")
23
+ driver.visit '/'
28
24
  end
29
25
 
30
- after(:all) { @driver.reset! }
31
-
32
26
  def render(options)
33
27
  FileUtils.rm_f @file_name
34
- @driver.render @file_name, options
28
+ driver.render @file_name, options
35
29
 
36
30
  @image = MiniMagick::Image.open @file_name
37
31
  end
38
32
 
39
33
  context "with default options" do
40
- before(:all){ render({}) }
34
+ before { render({}) }
41
35
 
42
36
  it "should be a PNG" do
43
37
  @image[:format].should == "PNG"
@@ -54,7 +48,7 @@ describe Capybara::Driver::Webkit, "rendering an image" do
54
48
  end
55
49
 
56
50
  context "with dimensions set larger than necessary" do
57
- before(:all){ render(:width => 500, :height => 400) }
51
+ before { render(:width => 500, :height => 400) }
58
52
 
59
53
  it "width should match the width given" do
60
54
  @image[:width].should == 500
@@ -66,7 +60,7 @@ describe Capybara::Driver::Webkit, "rendering an image" do
66
60
  end
67
61
 
68
62
  context "with dimensions set smaller than the document's default" do
69
- before(:all){ render(:width => 50, :height => 10) }
63
+ before { render(:width => 50, :height => 10) }
70
64
 
71
65
  it "width should be greater than the width given" do
72
66
  @image[:width].should > 50
@@ -76,5 +70,4 @@ describe Capybara::Driver::Webkit, "rendering an image" do
76
70
  @image[:height].should > 10
77
71
  end
78
72
  end
79
-
80
73
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'capybara/driver/webkit'
2
+ require 'capybara/webkit/driver'
3
3
 
4
- describe Capybara::Driver::Webkit, "#resize_window(width, height)" do
4
+ describe Capybara::Webkit::Driver, "#resize_window(width, height)" do
5
5
 
6
6
  before(:all) do
7
7
  app = lambda do |env|
@@ -27,7 +27,7 @@ describe Capybara::Driver::Webkit, "#resize_window(width, height)" do
27
27
  ]
28
28
  end
29
29
 
30
- @driver = Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
30
+ @driver = Capybara::Webkit::Driver.new(app, :browser => $webkit_browser)
31
31
  end
32
32
 
33
33
  DEFAULT_DIMENTIONS = "[1680x1050]"
@@ -1,117 +1,154 @@
1
1
  require 'spec_helper'
2
- require 'capybara/driver/webkit'
2
+ require 'capybara/webkit/driver'
3
+ require 'base64'
3
4
 
4
- describe Capybara::Driver::Webkit do
5
- subject { Capybara::Driver::Webkit.new(@app, :browser => $webkit_browser) }
6
- before { subject.visit("/hello/world?success=true") }
7
- after { subject.reset! }
5
+ describe Capybara::Webkit::Driver do
6
+ include AppRunner
8
7
 
9
8
  context "iframe app" do
10
- before(:all) do
11
- @app = lambda do |env|
12
- params = ::Rack::Utils.parse_query(env['QUERY_STRING'])
13
- if params["iframe"] == "true"
14
- # We are in an iframe request.
15
- p_id = "farewell"
16
- msg = "goodbye"
17
- iframe = nil
18
- else
19
- # We are not in an iframe request and need to make an iframe!
20
- p_id = "greeting"
21
- msg = "hello"
22
- iframe = "<iframe id=\"f\" src=\"/?iframe=true\"></iframe>"
9
+ let(:driver) do
10
+ driver_for_app do
11
+ get "/" do
12
+ if in_iframe_request?
13
+ p_id = "farewell"
14
+ msg = "goodbye"
15
+ iframe = nil
16
+ else
17
+ p_id = "greeting"
18
+ msg = "hello"
19
+ iframe = "<iframe id=\"f\" src=\"/?iframe=true\"></iframe>"
20
+ end
21
+ <<-HTML
22
+ <html>
23
+ <head>
24
+ <style type="text/css">
25
+ #display_none { display: none }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ #{iframe}
30
+ <script type="text/javascript">
31
+ document.write("<p id='#{p_id}'>#{msg}</p>");
32
+ </script>
33
+ </body>
34
+ </html>
35
+ HTML
36
+ end
37
+
38
+ def in_iframe_request?
39
+ params[:iframe] == "true"
23
40
  end
24
- body = <<-HTML
25
- <html>
26
- <head>
27
- <style type="text/css">
28
- #display_none { display: none }
29
- </style>
30
- </head>
31
- <body>
32
- #{iframe}
33
- <script type="text/javascript">
34
- document.write("<p id='#{p_id}'>#{msg}</p>");
35
- </script>
36
- </body>
37
- </html>
38
- HTML
39
- [200,
40
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
41
- [body]]
42
41
  end
43
42
  end
44
43
 
44
+ before do
45
+ driver.visit("/")
46
+ end
47
+
45
48
  it "finds frames by index" do
46
- subject.within_frame(0) do
47
- subject.find("//*[contains(., 'goodbye')]").should_not be_empty
49
+ driver.within_frame(0) do
50
+ driver.find("//*[contains(., 'goodbye')]").should_not be_empty
48
51
  end
49
52
  end
50
53
 
51
54
  it "finds frames by id" do
52
- subject.within_frame("f") do
53
- subject.find("//*[contains(., 'goodbye')]").should_not be_empty
55
+ driver.within_frame("f") do
56
+ driver.find("//*[contains(., 'goodbye')]").should_not be_empty
54
57
  end
55
58
  end
56
59
 
57
60
  it "raises error for missing frame by index" do
58
- expect { subject.within_frame(1) { } }.
59
- to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError)
61
+ expect { driver.within_frame(1) { } }.
62
+ to raise_error(Capybara::Webkit::InvalidResponseError)
60
63
  end
61
64
 
62
65
  it "raise_error for missing frame by id" do
63
- expect { subject.within_frame("foo") { } }.
64
- to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError)
66
+ expect { driver.within_frame("foo") { } }.
67
+ to raise_error(Capybara::Webkit::InvalidResponseError)
65
68
  end
66
69
 
67
70
  it "returns an attribute's value" do
68
- subject.within_frame("f") do
69
- subject.find("//p").first["id"].should == "farewell"
71
+ driver.within_frame("f") do
72
+ driver.find("//p").first["id"].should == "farewell"
70
73
  end
71
74
  end
72
75
 
76
+ it "returns an attribute's innerHTML" do
77
+ driver.find('//body').first.inner_html.should =~ %r{<iframe.*</iframe>.*<script.*</script>.*}m
78
+ end
79
+
80
+ it "receive an attribute's innerHTML" do
81
+ driver.find('//body').first.inner_html = 'foobar'
82
+ driver.find("//body[contains(., 'foobar')]").should_not be_empty
83
+ end
84
+
73
85
  it "returns a node's text" do
74
- subject.within_frame("f") do
75
- subject.find("//p").first.text.should == "goodbye"
86
+ driver.within_frame("f") do
87
+ driver.find("//p").first.text.should == "goodbye"
76
88
  end
77
89
  end
78
90
 
79
91
  it "returns the current URL" do
80
- subject.within_frame("f") do
81
- port = subject.instance_variable_get("@rack_server").port
82
- subject.current_url.should == "http://127.0.0.1:#{port}/?iframe=true"
92
+ driver.within_frame("f") do
93
+ driver.current_url.should == driver_url(driver, "/?iframe=true")
83
94
  end
84
95
  end
85
96
 
86
97
  it "returns the source code for the page" do
87
- subject.within_frame("f") do
88
- subject.source.should =~ %r{<html>.*farewell.*}m
98
+ driver.within_frame("f") do
99
+ driver.source.should =~ %r{<html>.*farewell.*}m
89
100
  end
90
101
  end
91
102
 
92
103
  it "evaluates Javascript" do
93
- subject.within_frame("f") do
94
- result = subject.evaluate_script(%<document.getElementById('farewell').innerText>)
104
+ driver.within_frame("f") do
105
+ result = driver.evaluate_script(%<document.getElementById('farewell').innerText>)
95
106
  result.should == "goodbye"
96
107
  end
97
108
  end
98
109
 
99
110
  it "executes Javascript" do
100
- subject.within_frame("f") do
101
- subject.execute_script(%<document.getElementById('farewell').innerHTML = 'yo'>)
102
- subject.find("//p[contains(., 'yo')]").should_not be_empty
111
+ driver.within_frame("f") do
112
+ driver.execute_script(%<document.getElementById('farewell').innerHTML = 'yo'>)
113
+ driver.find("//p[contains(., 'yo')]").should_not be_empty
114
+ end
115
+ end
116
+ end
117
+
118
+ context "error iframe app" do
119
+ let(:driver) do
120
+ driver_for_app do
121
+ get "/inner-not-found" do
122
+ invalid_response
123
+ end
124
+
125
+ get "/" do
126
+ <<-HTML
127
+ <html>
128
+ <body>
129
+ <iframe src="/inner-not-found"></iframe>
130
+ </body>
131
+ </html>
132
+ HTML
133
+ end
103
134
  end
104
135
  end
136
+
137
+ it "raises error whose message references the actual missing url" do
138
+ expect { driver.visit("/") }.to raise_error(Capybara::Webkit::InvalidResponseError, /inner-not-found/)
139
+ end
105
140
  end
106
141
 
107
142
  context "redirect app" do
108
- before(:all) do
109
- @app = lambda do |env|
110
- if env['PATH_INFO'] == '/target'
111
- content_type = "<p>#{env['CONTENT_TYPE']}</p>"
112
- [200, {"Content-Type" => "text/html", "Content-Length" => content_type.length.to_s}, [content_type]]
113
- elsif env['PATH_INFO'] == '/form'
114
- body = <<-HTML
143
+ let(:driver) do
144
+ driver_for_app do
145
+ get '/target' do
146
+ headers 'X-Redirected' => 'true'
147
+ "<p>#{env['CONTENT_TYPE']}</p>"
148
+ end
149
+
150
+ get '/form' do
151
+ <<-HTML
115
152
  <html>
116
153
  <body>
117
154
  <form action="/redirect" method="POST" enctype="multipart/form-data">
@@ -120,369 +157,565 @@ describe Capybara::Driver::Webkit do
120
157
  </body>
121
158
  </html>
122
159
  HTML
123
- [200, {"Content-Type" => "text/html", "Content-Length" => body.length.to_s}, [body]]
124
- else
125
- [301, {"Location" => "/target"}, [""]]
160
+ end
161
+
162
+ post '/redirect' do
163
+ redirect '/target'
164
+ end
165
+
166
+ get '/redirect-me' do
167
+ redirect '/target'
126
168
  end
127
169
  end
128
170
  end
129
171
 
130
172
  it "should redirect without content type" do
131
- subject.visit("/form")
132
- subject.find("//input").first.click
133
- subject.find("//p").first.text.should == ""
173
+ driver.visit("/form")
174
+ driver.find("//input").first.click
175
+ driver.find("//p").first.text.should == ""
134
176
  end
135
177
 
136
178
  it "returns the current URL when changed by pushState after a redirect" do
137
- subject.visit("/redirect-me")
138
- port = subject.instance_variable_get("@rack_server").port
139
- subject.execute_script("window.history.pushState({}, '', '/pushed-after-redirect')")
140
- subject.current_url.should == "http://127.0.0.1:#{port}/pushed-after-redirect"
179
+ driver.visit("/redirect-me")
180
+ driver.current_url.should == driver_url(driver, "/target")
181
+ driver.execute_script("window.history.pushState({}, '', '/pushed-after-redirect')")
182
+ driver.current_url.should == driver_url(driver, "/pushed-after-redirect")
141
183
  end
142
184
 
143
185
  it "returns the current URL when changed by replaceState after a redirect" do
144
- subject.visit("/redirect-me")
145
- port = subject.instance_variable_get("@rack_server").port
146
- subject.execute_script("window.history.replaceState({}, '', '/replaced-after-redirect')")
147
- subject.current_url.should == "http://127.0.0.1:#{port}/replaced-after-redirect"
186
+ driver.visit("/redirect-me")
187
+ driver.current_url.should == driver_url(driver, "/target")
188
+ driver.execute_script("window.history.replaceState({}, '', '/replaced-after-redirect')")
189
+ driver.current_url.should == driver_url(driver, "/replaced-after-redirect")
190
+ end
191
+
192
+ it "should make headers available through response_headers" do
193
+ driver.visit('/redirect-me')
194
+ driver.response_headers['X-Redirected'].should == "true"
148
195
  end
196
+
197
+ it "should make the status code available through status_code" do
198
+ driver.visit('/redirect-me')
199
+ driver.status_code.should == 200
200
+ end
201
+
149
202
  end
150
203
 
151
204
  context "css app" do
152
- before(:all) do
153
- body = "css"
154
- @app = lambda do |env|
155
- [200, {"Content-Type" => "text/css", "Content-Length" => body.length.to_s}, [body]]
205
+ let(:driver) do
206
+ driver_for_app do
207
+ get "/" do
208
+ headers "Content-Type" => "text/css"
209
+ "css"
210
+ end
156
211
  end
157
- subject.visit("/")
158
212
  end
159
213
 
214
+ before { driver.visit("/") }
215
+
160
216
  it "renders unsupported content types gracefully" do
161
- subject.body.should =~ /css/
217
+ driver.body.should =~ /css/
162
218
  end
163
219
 
164
220
  it "sets the response headers with respect to the unsupported request" do
165
- subject.response_headers["Content-Type"].should == "text/css"
221
+ driver.response_headers["Content-Type"].should == "text/css"
166
222
  end
167
223
  end
168
224
 
169
225
  context "hello app" do
170
- before(:all) do
171
- @app = lambda do |env|
172
- body = <<-HTML
173
- <html>
174
- <head>
175
- <style type="text/css">
176
- #display_none { display: none }
177
- </style>
178
- </head>
179
- <body>
180
- <div class='normalize'>Spaces&nbsp;not&nbsp;normalized&nbsp;</div>
181
- <div id="display_none">
182
- <div id="invisible">Can't see me</div>
183
- </div>
184
- <input type="text" disabled="disabled"/>
185
- <input id="checktest" type="checkbox" checked="checked"/>
186
- <script type="text/javascript">
187
- document.write("<p id='greeting'>he" + "llo</p>");
188
- </script>
189
- </body>
190
- </html>
191
- HTML
192
- [200,
193
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
194
- [body]]
195
- end
226
+ let(:driver) do
227
+ driver_for_html(<<-HTML)
228
+ <html>
229
+ <head>
230
+ <style type="text/css">
231
+ #display_none { display: none }
232
+ #visibility_hidden { visibility: hidden }
233
+ </style>
234
+ </head>
235
+ <body>
236
+ <div class='normalize'>Spaces&nbsp;not&nbsp;normalized&nbsp;</div>
237
+ <div id="display_none">
238
+ <div id="invisible">Can't see me</div>
239
+ </div>
240
+ <div id="visibility_hidden">
241
+ <div id="invisible_with_visibility">Can't see me too</div>
242
+ </div>
243
+ <input type="text" disabled="disabled"/>
244
+ <input id="checktest" type="checkbox" checked="checked"/>
245
+ <script type="text/javascript">
246
+ document.write("<p id='greeting'>he" + "llo</p>");
247
+ </script>
248
+ </body>
249
+ </html>
250
+ HTML
196
251
  end
197
252
 
253
+ before { driver.visit("/") }
254
+
198
255
  it "handles anchor tags" do
199
- subject.visit("#test")
200
- subject.find("//*[contains(., 'hello')]").should_not be_empty
201
- subject.visit("#test")
202
- subject.find("//*[contains(., 'hello')]").should_not be_empty
256
+ driver.visit("#test")
257
+ driver.find("//*[contains(., 'hello')]").should_not be_empty
258
+ driver.visit("#test")
259
+ driver.find("//*[contains(., 'hello')]").should_not be_empty
203
260
  end
204
261
 
205
262
  it "finds content after loading a URL" do
206
- subject.find("//*[contains(., 'hello')]").should_not be_empty
263
+ driver.find("//*[contains(., 'hello')]").should_not be_empty
207
264
  end
208
265
 
209
266
  it "has an empty page after reseting" do
210
- subject.reset!
211
- subject.find("//*[contains(., 'hello')]").should be_empty
267
+ driver.reset!
268
+ driver.find("//*[contains(., 'hello')]").should be_empty
212
269
  end
213
270
 
214
- it "has a location of 'about:blank' after reseting" do
215
- subject.reset!
216
- subject.current_url.should == "about:blank"
271
+ it "has a blank location after reseting" do
272
+ driver.reset!
273
+ driver.current_url.should == "about:blank"
217
274
  end
218
275
 
219
276
  it "raises an error for an invalid xpath query" do
220
- expect { subject.find("totally invalid salad") }.
221
- to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError, /xpath/i)
277
+ expect { driver.find("totally invalid salad") }.
278
+ to raise_error(Capybara::Webkit::InvalidResponseError, /xpath/i)
222
279
  end
223
280
 
224
281
  it "returns an attribute's value" do
225
- subject.find("//p").first["id"].should == "greeting"
282
+ driver.find("//p").first["id"].should == "greeting"
226
283
  end
227
284
 
228
285
  it "parses xpath with quotes" do
229
- subject.find('//*[contains(., "hello")]').should_not be_empty
286
+ driver.find('//*[contains(., "hello")]').should_not be_empty
230
287
  end
231
288
 
232
289
  it "returns a node's text" do
233
- subject.find("//p").first.text.should == "hello"
290
+ driver.find("//p").first.text.should == "hello"
234
291
  end
235
292
 
236
293
  it "normalizes a node's text" do
237
- subject.find("//div[contains(@class, 'normalize')]").first.text.should == "Spaces not normalized"
294
+ driver.find("//div[contains(@class, 'normalize')]").first.text.should == "Spaces not normalized"
238
295
  end
239
296
 
240
297
  it "returns the current URL" do
241
- port = subject.instance_variable_get("@rack_server").port
242
- subject.current_url.should == "http://127.0.0.1:#{port}/hello/world?success=true"
298
+ driver.visit "/hello/world?success=true"
299
+ driver.current_url.should == driver_url(driver, "/hello/world?success=true")
243
300
  end
244
301
 
245
302
  it "returns the current URL when changed by pushState" do
246
- port = subject.instance_variable_get("@rack_server").port
247
- subject.execute_script("window.history.pushState({}, '', '/pushed')")
248
- subject.current_url.should == "http://127.0.0.1:#{port}/pushed"
303
+ driver.execute_script("window.history.pushState({}, '', '/pushed')")
304
+ driver.current_url.should == driver_url(driver, "/pushed")
249
305
  end
250
306
 
251
307
  it "returns the current URL when changed by replaceState" do
252
- port = subject.instance_variable_get("@rack_server").port
253
- subject.execute_script("window.history.replaceState({}, '', '/replaced')")
254
- subject.current_url.should == "http://127.0.0.1:#{port}/replaced"
308
+ driver.execute_script("window.history.replaceState({}, '', '/replaced')")
309
+ driver.current_url.should == driver_url(driver, "/replaced")
255
310
  end
256
311
 
257
312
  it "does not double-encode URLs" do
258
- subject.visit("/hello/world?success=%25true")
259
- subject.current_url.should =~ /success=\%25true/
313
+ driver.visit("/hello/world?success=%25true")
314
+ driver.current_url.should =~ /success=\%25true/
260
315
  end
261
316
 
262
317
  it "visits a page with an anchor" do
263
- subject.visit("/hello#display_none")
264
- subject.current_url.should =~ /hello#display_none/
318
+ driver.visit("/hello#display_none")
319
+ driver.current_url.should =~ /hello#display_none/
265
320
  end
266
321
 
267
322
  it "returns the source code for the page" do
268
- subject.source.should =~ %r{<html>.*greeting.*}m
323
+ driver.source.should =~ %r{<html>.*greeting.*}m
269
324
  end
270
325
 
271
326
  it "evaluates Javascript and returns a string" do
272
- result = subject.evaluate_script(%<document.getElementById('greeting').innerText>)
327
+ result = driver.evaluate_script(%<document.getElementById('greeting').innerText>)
273
328
  result.should == "hello"
274
329
  end
275
330
 
276
331
  it "evaluates Javascript and returns an array" do
277
- result = subject.evaluate_script(%<["hello", "world"]>)
332
+ result = driver.evaluate_script(%<["hello", "world"]>)
278
333
  result.should == %w(hello world)
279
334
  end
280
335
 
281
336
  it "evaluates Javascript and returns an int" do
282
- result = subject.evaluate_script(%<123>)
337
+ result = driver.evaluate_script(%<123>)
283
338
  result.should == 123
284
339
  end
285
340
 
286
341
  it "evaluates Javascript and returns a float" do
287
- result = subject.evaluate_script(%<1.5>)
342
+ result = driver.evaluate_script(%<1.5>)
288
343
  result.should == 1.5
289
344
  end
290
345
 
291
346
  it "evaluates Javascript and returns null" do
292
- result = subject.evaluate_script(%<(function () {})()>)
347
+ result = driver.evaluate_script(%<(function () {})()>)
293
348
  result.should == nil
294
349
  end
295
350
 
296
351
  it "evaluates Javascript and returns an object" do
297
- result = subject.evaluate_script(%<({ 'one' : 1 })>)
352
+ result = driver.evaluate_script(%<({ 'one' : 1 })>)
298
353
  result.should == { 'one' => 1 }
299
354
  end
300
355
 
301
356
  it "evaluates Javascript and returns true" do
302
- result = subject.evaluate_script(%<true>)
357
+ result = driver.evaluate_script(%<true>)
303
358
  result.should === true
304
359
  end
305
360
 
306
361
  it "evaluates Javascript and returns false" do
307
- result = subject.evaluate_script(%<false>)
362
+ result = driver.evaluate_script(%<false>)
308
363
  result.should === false
309
364
  end
310
365
 
311
366
  it "evaluates Javascript and returns an escaped string" do
312
- result = subject.evaluate_script(%<'"'>)
367
+ result = driver.evaluate_script(%<'"'>)
313
368
  result.should === "\""
314
369
  end
315
370
 
316
371
  it "evaluates Javascript with multiple lines" do
317
- result = subject.evaluate_script("[1,\n2]")
372
+ result = driver.evaluate_script("[1,\n2]")
318
373
  result.should == [1, 2]
319
374
  end
320
375
 
321
376
  it "executes Javascript" do
322
- subject.execute_script(%<document.getElementById('greeting').innerHTML = 'yo'>)
323
- subject.find("//p[contains(., 'yo')]").should_not be_empty
377
+ driver.execute_script(%<document.getElementById('greeting').innerHTML = 'yo'>)
378
+ driver.find("//p[contains(., 'yo')]").should_not be_empty
324
379
  end
325
380
 
326
381
  it "raises an error for failing Javascript" do
327
- expect { subject.execute_script(%<invalid salad>) }.
328
- to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError)
382
+ expect { driver.execute_script(%<invalid salad>) }.
383
+ to raise_error(Capybara::Webkit::InvalidResponseError)
329
384
  end
330
385
 
331
386
  it "doesn't raise an error for Javascript that doesn't return anything" do
332
- lambda { subject.execute_script(%<(function () { "returns nothing" })()>) }.
387
+ lambda { driver.execute_script(%<(function () { "returns nothing" })()>) }.
333
388
  should_not raise_error
334
389
  end
335
390
 
336
391
  it "returns a node's tag name" do
337
- subject.find("//p").first.tag_name.should == "p"
392
+ driver.find("//p").first.tag_name.should == "p"
338
393
  end
339
394
 
340
395
  it "reads disabled property" do
341
- subject.find("//input").first.should be_disabled
396
+ driver.find("//input").first.should be_disabled
342
397
  end
343
398
 
344
399
  it "reads checked property" do
345
- subject.find("//input[@id='checktest']").first.should be_checked
400
+ driver.find("//input[@id='checktest']").first.should be_checked
346
401
  end
347
402
 
348
403
  it "finds visible elements" do
349
- subject.find("//p").first.should be_visible
350
- subject.find("//*[@id='invisible']").first.should_not be_visible
404
+ driver.find("//p").first.should be_visible
405
+ driver.find("//*[@id='invisible']").first.should_not be_visible
406
+ driver.find("//*[@id='invisible_with_visibility']").first.should_not be_visible
351
407
  end
352
408
  end
353
409
 
354
410
  context "console messages app" do
411
+ let(:driver) do
412
+ driver_for_html(<<-HTML)
413
+ <html>
414
+ <head>
415
+ </head>
416
+ <body>
417
+ <script type="text/javascript">
418
+ console.log("hello");
419
+ console.log("hello again");
420
+ oops
421
+ </script>
422
+ </body>
423
+ </html>
424
+ HTML
425
+ end
426
+
427
+ before { driver.visit("/") }
428
+
429
+ it "collects messages logged to the console" do
430
+ url = driver_url(driver, "/")
431
+ message = driver.console_messages.first
432
+ message.should include :source => url, :message => "hello"
433
+ # QtWebKit returns different line numbers depending on the version
434
+ [5, 6].should include(message[:line_number])
435
+ driver.console_messages.length.should eq 3
436
+ end
437
+
438
+ it "logs errors to the console" do
439
+ driver.error_messages.length.should eq 1
440
+ end
355
441
 
356
- before(:all) do
357
- @app = lambda do |env|
358
- body = <<-HTML
442
+ it "empties the array when reset" do
443
+ driver.reset!
444
+ driver.console_messages.should be_empty
445
+ end
446
+
447
+ end
448
+
449
+ context "javascript dialog interaction" do
450
+ context "on an alert app" do
451
+ let(:driver) do
452
+ driver_for_html(<<-HTML)
359
453
  <html>
360
454
  <head>
361
455
  </head>
362
456
  <body>
363
457
  <script type="text/javascript">
364
- console.log("hello");
365
- console.log("hello again");
366
- oops
458
+ alert("Alert Text Goes Here");
367
459
  </script>
368
460
  </body>
369
461
  </html>
370
462
  HTML
371
- [200,
372
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
373
- [body]]
374
463
  end
375
- end
376
464
 
377
- it "collects messages logged to the console" do
378
- subject.console_messages.first.should include :source, :message => "hello", :line_number => 6
379
- subject.console_messages.length.should eq 3
380
- end
465
+ before { driver.visit("/") }
381
466
 
382
- it "logs errors to the console" do
383
- subject.error_messages.length.should eq 1
467
+ it "should let me read my alert messages" do
468
+ driver.alert_messages.first.should == "Alert Text Goes Here"
469
+ end
470
+
471
+ it "empties the array when reset" do
472
+ driver.reset!
473
+ driver.alert_messages.should be_empty
474
+ end
384
475
  end
385
476
 
386
- end
477
+ context "on a confirm app" do
478
+ let(:driver) do
479
+ driver_for_html(<<-HTML)
480
+ <html>
481
+ <head>
482
+ </head>
483
+ <body>
484
+ <script type="text/javascript">
485
+ function test_dialog() {
486
+ if(confirm("Yes?"))
487
+ console.log("hello");
488
+ else
489
+ console.log("goodbye");
490
+ }
491
+ </script>
492
+ <input type="button" onclick="test_dialog()" name="test"/>
493
+ </body>
494
+ </html>
495
+ HTML
496
+ end
387
497
 
388
- context "form app" do
389
- before(:all) do
390
- @app = lambda do |env|
391
- body = <<-HTML
392
- <html><body>
393
- <form action="/" method="GET">
394
- <input type="text" name="foo" value="bar"/>
395
- <input type="text" name="maxlength_foo" value="bar" maxlength="10"/>
396
- <input type="text" id="disabled_input" disabled="disabled"/>
397
- <input type="checkbox" name="checkedbox" value="1" checked="checked"/>
398
- <input type="checkbox" name="uncheckedbox" value="2"/>
399
- <select name="animal">
400
- <option id="select-option-monkey">Monkey</option>
401
- <option id="select-option-capybara" selected="selected">Capybara</option>
402
- </select>
403
- <select name="toppings" multiple="multiple">
404
- <optgroup label="Mediocre Toppings">
405
- <option selected="selected" id="topping-apple">Apple</option>
406
- <option selected="selected" id="topping-banana">Banana</option>
407
- </optgroup>
408
- <optgroup label="Best Toppings">
409
- <option selected="selected" id="topping-cherry">Cherry</option>
410
- </optgroup>
411
- </select>
412
- <textarea id="only-textarea">what a wonderful area for text</textarea>
413
- <input type="radio" id="only-radio" value="1"/>
414
- <button type="reset">Reset Form</button>
415
- </form>
416
- </body></html>
498
+ before { driver.visit("/") }
499
+
500
+ it "should default to accept the confirm" do
501
+ driver.find("//input").first.click
502
+ driver.console_messages.first[:message].should == "hello"
503
+ end
504
+
505
+ it "can dismiss the confirm" do
506
+ driver.dismiss_js_confirms!
507
+ driver.find("//input").first.click
508
+ driver.console_messages.first[:message].should == "goodbye"
509
+ end
510
+
511
+ it "can accept the confirm explicitly" do
512
+ driver.dismiss_js_confirms!
513
+ driver.accept_js_confirms!
514
+ driver.find("//input").first.click
515
+ driver.console_messages.first[:message].should == "hello"
516
+ end
517
+
518
+ it "should collect the javsacript confirm dialog contents" do
519
+ driver.find("//input").first.click
520
+ driver.confirm_messages.first.should == "Yes?"
521
+ end
522
+
523
+ it "empties the array when reset" do
524
+ driver.find("//input").first.click
525
+ driver.reset!
526
+ driver.confirm_messages.should be_empty
527
+ end
528
+
529
+ it "resets to the default of accepting confirms" do
530
+ driver.dismiss_js_confirms!
531
+ driver.reset!
532
+ driver.visit("/")
533
+ driver.find("//input").first.click
534
+ driver.console_messages.first[:message].should == "hello"
535
+ end
536
+ end
537
+
538
+ context "on a prompt app" do
539
+ let(:driver) do
540
+ driver_for_html(<<-HTML)
541
+ <html>
542
+ <head>
543
+ </head>
544
+ <body>
545
+ <script type="text/javascript">
546
+ function test_dialog() {
547
+ var response = prompt("Your name?", "John Smith");
548
+ if(response != null)
549
+ console.log("hello " + response);
550
+ else
551
+ console.log("goodbye");
552
+ }
553
+ </script>
554
+ <input type="button" onclick="test_dialog()" name="test"/>
555
+ </body>
556
+ </html>
417
557
  HTML
418
- [200,
419
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
420
- [body]]
558
+ end
559
+
560
+ before { driver.visit("/") }
561
+
562
+ it "should default to dismiss the prompt" do
563
+ driver.find("//input").first.click
564
+ driver.console_messages.first[:message].should == "goodbye"
565
+ end
566
+
567
+ it "can accept the prompt without providing text" do
568
+ driver.accept_js_prompts!
569
+ driver.find("//input").first.click
570
+ driver.console_messages.first[:message].should == "hello John Smith"
571
+ end
572
+
573
+ it "can accept the prompt with input" do
574
+ driver.js_prompt_input = "Capy"
575
+ driver.accept_js_prompts!
576
+ driver.find("//input").first.click
577
+ driver.console_messages.first[:message].should == "hello Capy"
578
+ end
579
+
580
+ it "can return to dismiss the prompt after accepting prompts" do
581
+ driver.accept_js_prompts!
582
+ driver.dismiss_js_prompts!
583
+ driver.find("//input").first.click
584
+ driver.console_messages.first[:message].should == "goodbye"
585
+ end
586
+
587
+ it "should let me remove the prompt input text" do
588
+ driver.js_prompt_input = "Capy"
589
+ driver.accept_js_prompts!
590
+ driver.find("//input").first.click
591
+ driver.console_messages.first[:message].should == "hello Capy"
592
+ driver.js_prompt_input = nil
593
+ driver.find("//input").first.click
594
+ driver.console_messages.last[:message].should == "hello John Smith"
595
+ end
596
+
597
+ it "should collect the javsacript prompt dialog contents" do
598
+ driver.find("//input").first.click
599
+ driver.prompt_messages.first.should == "Your name?"
600
+ end
601
+
602
+ it "empties the array when reset" do
603
+ driver.find("//input").first.click
604
+ driver.reset!
605
+ driver.prompt_messages.should be_empty
606
+ end
607
+
608
+ it "returns the prompt action to dismiss on reset" do
609
+ driver.accept_js_prompts!
610
+ driver.reset!
611
+ driver.visit("/")
612
+ driver.find("//input").first.click
613
+ driver.console_messages.first[:message].should == "goodbye"
421
614
  end
422
615
  end
616
+ end
617
+
618
+ context "form app" do
619
+ let(:driver) do
620
+ driver_for_html(<<-HTML)
621
+ <html><body>
622
+ <form action="/" method="GET">
623
+ <input type="text" name="foo" value="bar"/>
624
+ <input type="text" name="maxlength_foo" value="bar" maxlength="10"/>
625
+ <input type="text" id="disabled_input" disabled="disabled"/>
626
+ <input type="checkbox" name="checkedbox" value="1" checked="checked"/>
627
+ <input type="checkbox" name="uncheckedbox" value="2"/>
628
+ <select name="animal">
629
+ <option id="select-option-monkey">Monkey</option>
630
+ <option id="select-option-capybara" selected="selected">Capybara</option>
631
+ </select>
632
+ <select name="toppings" multiple="multiple">
633
+ <optgroup label="Mediocre Toppings">
634
+ <option selected="selected" id="topping-apple">Apple</option>
635
+ <option selected="selected" id="topping-banana">Banana</option>
636
+ </optgroup>
637
+ <optgroup label="Best Toppings">
638
+ <option selected="selected" id="topping-cherry">Cherry</option>
639
+ </optgroup>
640
+ </select>
641
+ <select name="guitars" multiple>
642
+ <option selected="selected" id="fender">Fender</option>
643
+ <option selected="selected" id="gibson">Gibson</option>
644
+ </select>
645
+ <textarea id="only-textarea">what a wonderful area for text</textarea>
646
+ <input type="radio" id="only-radio" value="1"/>
647
+ <button type="reset">Reset Form</button>
648
+ </form>
649
+ </body></html>
650
+ HTML
651
+ end
652
+
653
+ before { driver.visit("/") }
423
654
 
424
655
  it "returns a textarea's value" do
425
- subject.find("//textarea").first.value.should == "what a wonderful area for text"
656
+ driver.find("//textarea").first.value.should == "what a wonderful area for text"
426
657
  end
427
658
 
428
659
  it "returns a text input's value" do
429
- subject.find("//input").first.value.should == "bar"
660
+ driver.find("//input").first.value.should == "bar"
430
661
  end
431
662
 
432
663
  it "returns a select's value" do
433
- subject.find("//select").first.value.should == "Capybara"
664
+ driver.find("//select").first.value.should == "Capybara"
434
665
  end
435
666
 
436
667
  it "sets an input's value" do
437
- input = subject.find("//input").first
668
+ input = driver.find("//input").first
438
669
  input.set("newvalue")
439
670
  input.value.should == "newvalue"
440
671
  end
441
672
 
442
673
  it "sets an input's value greater than the max length" do
443
- input = subject.find("//input[@name='maxlength_foo']").first
674
+ input = driver.find("//input[@name='maxlength_foo']").first
444
675
  input.set("allegories (poems)")
445
676
  input.value.should == "allegories"
446
677
  end
447
678
 
448
679
  it "sets an input's value equal to the max length" do
449
- input = subject.find("//input[@name='maxlength_foo']").first
680
+ input = driver.find("//input[@name='maxlength_foo']").first
450
681
  input.set("allegories")
451
682
  input.value.should == "allegories"
452
683
  end
453
684
 
454
685
  it "sets an input's value less than the max length" do
455
- input = subject.find("//input[@name='maxlength_foo']").first
686
+ input = driver.find("//input[@name='maxlength_foo']").first
456
687
  input.set("poems")
457
688
  input.value.should == "poems"
458
689
  end
459
690
 
460
691
  it "sets an input's nil value" do
461
- input = subject.find("//input").first
692
+ input = driver.find("//input").first
462
693
  input.set(nil)
463
694
  input.value.should == ""
464
695
  end
465
696
 
466
697
  it "sets a select's value" do
467
- select = subject.find("//select").first
698
+ select = driver.find("//select").first
468
699
  select.set("Monkey")
469
700
  select.value.should == "Monkey"
470
701
  end
471
702
 
472
703
  it "sets a textarea's value" do
473
- textarea = subject.find("//textarea").first
704
+ textarea = driver.find("//textarea").first
474
705
  textarea.set("newvalue")
475
706
  textarea.value.should == "newvalue"
476
707
  end
477
708
 
478
- let(:monkey_option) { subject.find("//option[@id='select-option-monkey']").first }
479
- let(:capybara_option) { subject.find("//option[@id='select-option-capybara']").first }
480
- let(:animal_select) { subject.find("//select[@name='animal']").first }
481
- let(:apple_option) { subject.find("//option[@id='topping-apple']").first }
482
- let(:banana_option) { subject.find("//option[@id='topping-banana']").first }
483
- let(:cherry_option) { subject.find("//option[@id='topping-cherry']").first }
484
- let(:toppings_select) { subject.find("//select[@name='toppings']").first }
485
- let(:reset_button) { subject.find("//button[@type='reset']").first }
709
+ let(:monkey_option) { driver.find("//option[@id='select-option-monkey']").first }
710
+ let(:capybara_option) { driver.find("//option[@id='select-option-capybara']").first }
711
+ let(:animal_select) { driver.find("//select[@name='animal']").first }
712
+ let(:apple_option) { driver.find("//option[@id='topping-apple']").first }
713
+ let(:banana_option) { driver.find("//option[@id='topping-banana']").first }
714
+ let(:cherry_option) { driver.find("//option[@id='topping-cherry']").first }
715
+ let(:toppings_select) { driver.find("//select[@name='toppings']").first }
716
+ let(:guitars_select) { driver.find("//select[@name='guitars']").first }
717
+ let(:fender_option) { driver.find("//option[@id='fender']").first }
718
+ let(:reset_button) { driver.find("//button[@type='reset']").first }
486
719
 
487
720
  context "a select element's selection has been changed" do
488
721
  before do
@@ -523,6 +756,18 @@ describe Capybara::Driver::Webkit do
523
756
  end
524
757
  end
525
758
 
759
+ context "a multi-select (with empty multiple attribute) element's option has been unselected" do
760
+ before do
761
+ guitars_select.value.should include("Fender", "Gibson")
762
+
763
+ fender_option.unselect_option
764
+ end
765
+
766
+ it "does not return the deselected option" do
767
+ guitars_select.value.should_not include("Fender")
768
+ end
769
+ end
770
+
526
771
  it "reselects an option in a multi-select" do
527
772
  apple_option.unselect_option
528
773
  banana_option.unselect_option
@@ -537,8 +782,8 @@ describe Capybara::Driver::Webkit do
537
782
  toppings_select.value.should include("Apple", "Banana", "Cherry")
538
783
  end
539
784
 
540
- let(:checked_box) { subject.find("//input[@name='checkedbox']").first }
541
- let(:unchecked_box) { subject.find("//input[@name='uncheckedbox']").first }
785
+ let(:checked_box) { driver.find("//input[@name='checkedbox']").first }
786
+ let(:unchecked_box) { driver.find("//input[@name='uncheckedbox']").first }
542
787
 
543
788
  it "knows a checked box is checked" do
544
789
  checked_box['checked'].should be_true
@@ -576,8 +821,8 @@ describe Capybara::Driver::Webkit do
576
821
  unchecked_box.should_not be_checked
577
822
  end
578
823
 
579
- let(:enabled_input) { subject.find("//input[@name='foo']").first }
580
- let(:disabled_input) { subject.find("//input[@id='disabled_input']").first }
824
+ let(:enabled_input) { driver.find("//input[@name='foo']").first }
825
+ let(:disabled_input) { driver.find("//input[@id='disabled_input']").first }
581
826
 
582
827
  it "knows a disabled input is disabled" do
583
828
  disabled_input['disabled'].should be_true
@@ -589,267 +834,254 @@ describe Capybara::Driver::Webkit do
589
834
  end
590
835
 
591
836
  context "dom events" do
592
- before(:all) do
593
- @app = lambda do |env|
594
- body = <<-HTML
595
-
596
- <html><body>
597
- <a href='#' class='watch'>Link</a>
598
- <ul id="events"></ul>
599
- <script type="text/javascript">
600
- var events = document.getElementById("events");
601
- var recordEvent = function (event) {
602
- var element = document.createElement("li");
603
- element.innerHTML = event.type;
604
- events.appendChild(element);
605
- };
606
-
607
- var elements = document.getElementsByClassName("watch");
608
- for (var i = 0; i < elements.length; i++) {
609
- var element = elements[i];
610
- element.addEventListener("mousedown", recordEvent);
611
- element.addEventListener("mouseup", recordEvent);
612
- element.addEventListener("click", recordEvent);
613
- }
614
- </script>
615
- </body></html>
616
- HTML
617
- [200,
618
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
619
- [body]]
620
- end
621
- end
837
+ let(:driver) do
838
+ driver_for_html(<<-HTML)
839
+ <html><body>
840
+ <a href='#' class='watch'>Link</a>
841
+ <ul id="events"></ul>
842
+ <script type="text/javascript">
843
+ var events = document.getElementById("events");
844
+ var recordEvent = function (event) {
845
+ var element = document.createElement("li");
846
+ element.innerHTML = event.type;
847
+ events.appendChild(element);
848
+ };
849
+
850
+ var elements = document.getElementsByClassName("watch");
851
+ for (var i = 0; i < elements.length; i++) {
852
+ var element = elements[i];
853
+ element.addEventListener("mousedown", recordEvent);
854
+ element.addEventListener("mouseup", recordEvent);
855
+ element.addEventListener("click", recordEvent);
856
+ }
857
+ </script>
858
+ </body></html>
859
+ HTML
860
+ end
861
+
862
+ before { driver.visit("/") }
622
863
 
623
864
  it "triggers mouse events" do
624
- subject.find("//a").first.click
625
- subject.find("//li").map(&:text).should == %w(mousedown mouseup click)
865
+ driver.find("//a").first.click
866
+ driver.find("//li").map(&:text).should == %w(mousedown mouseup click)
626
867
  end
627
868
  end
628
869
 
629
870
  context "form events app" do
630
- before(:all) do
631
- @app = lambda do |env|
632
- body = <<-HTML
633
- <html><body>
634
- <form action="/" method="GET">
635
- <input class="watch" type="email"/>
636
- <input class="watch" type="number"/>
637
- <input class="watch" type="password"/>
638
- <input class="watch" type="search"/>
639
- <input class="watch" type="tel"/>
640
- <input class="watch" type="text"/>
641
- <input class="watch" type="url"/>
642
- <textarea class="watch"></textarea>
643
- <input class="watch" type="checkbox"/>
644
- <input class="watch" type="radio"/>
645
- </form>
646
- <ul id="events"></ul>
647
- <script type="text/javascript">
648
- var events = document.getElementById("events");
649
- var recordEvent = function (event) {
650
- var element = document.createElement("li");
651
- element.innerHTML = event.type;
652
- events.appendChild(element);
653
- };
654
-
655
- var elements = document.getElementsByClassName("watch");
656
- for (var i = 0; i < elements.length; i++) {
657
- var element = elements[i];
658
- element.addEventListener("focus", recordEvent);
659
- element.addEventListener("keydown", recordEvent);
660
- element.addEventListener("keypress", recordEvent);
661
- element.addEventListener("keyup", recordEvent);
662
- element.addEventListener("input", recordEvent);
663
- element.addEventListener("change", recordEvent);
664
- element.addEventListener("blur", recordEvent);
665
- element.addEventListener("mousedown", recordEvent);
666
- element.addEventListener("mouseup", recordEvent);
667
- element.addEventListener("click", recordEvent);
668
- }
669
- </script>
670
- </body></html>
671
- HTML
672
- [200,
673
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
674
- [body]]
675
- end
676
- end
871
+ let(:driver) do
872
+ driver_for_html(<<-HTML)
873
+ <html><body>
874
+ <form action="/" method="GET">
875
+ <input class="watch" type="email"/>
876
+ <input class="watch" type="number"/>
877
+ <input class="watch" type="password"/>
878
+ <input class="watch" type="search"/>
879
+ <input class="watch" type="tel"/>
880
+ <input class="watch" type="text"/>
881
+ <input class="watch" type="url"/>
882
+ <textarea class="watch"></textarea>
883
+ <input class="watch" type="checkbox"/>
884
+ <input class="watch" type="radio"/>
885
+ </form>
886
+ <ul id="events"></ul>
887
+ <script type="text/javascript">
888
+ var events = document.getElementById("events");
889
+ var recordEvent = function (event) {
890
+ var element = document.createElement("li");
891
+ element.innerHTML = event.type;
892
+ events.appendChild(element);
893
+ };
894
+
895
+ var elements = document.getElementsByClassName("watch");
896
+ for (var i = 0; i < elements.length; i++) {
897
+ var element = elements[i];
898
+ element.addEventListener("focus", recordEvent);
899
+ element.addEventListener("keydown", recordEvent);
900
+ element.addEventListener("keypress", recordEvent);
901
+ element.addEventListener("keyup", recordEvent);
902
+ element.addEventListener("input", recordEvent);
903
+ element.addEventListener("change", recordEvent);
904
+ element.addEventListener("blur", recordEvent);
905
+ element.addEventListener("mousedown", recordEvent);
906
+ element.addEventListener("mouseup", recordEvent);
907
+ element.addEventListener("click", recordEvent);
908
+ }
909
+ </script>
910
+ </body></html>
911
+ HTML
912
+ end
913
+
914
+ before { driver.visit("/") }
677
915
 
678
916
  let(:newtext) { 'newvalue' }
679
917
 
680
918
  let(:keyevents) do
681
919
  (%w{focus} +
682
920
  newtext.length.times.collect { %w{keydown keypress keyup input} } +
683
- %w{change blur}).flatten
921
+ %w{change}).flatten
684
922
  end
685
923
 
686
924
  %w(email number password search tel text url).each do | field_type |
687
925
  it "triggers text input events on inputs of type #{field_type}" do
688
- subject.find("//input[@type='#{field_type}']").first.set(newtext)
689
- subject.find("//li").map(&:text).should == keyevents
926
+ driver.find("//input[@type='#{field_type}']").first.set(newtext)
927
+ driver.find("//li").map(&:text).should == keyevents
690
928
  end
691
929
  end
692
930
 
693
931
  it "triggers textarea input events" do
694
- subject.find("//textarea").first.set(newtext)
695
- subject.find("//li").map(&:text).should == keyevents
932
+ driver.find("//textarea").first.set(newtext)
933
+ driver.find("//li").map(&:text).should == keyevents
696
934
  end
697
935
 
698
936
  it "triggers radio input events" do
699
- subject.find("//input[@type='radio']").first.set(true)
700
- subject.find("//li").map(&:text).should == %w(mousedown mouseup change click)
937
+ driver.find("//input[@type='radio']").first.set(true)
938
+ driver.find("//li").map(&:text).should == %w(mousedown focus mouseup change click)
701
939
  end
702
940
 
703
941
  it "triggers checkbox events" do
704
- subject.find("//input[@type='checkbox']").first.set(true)
705
- subject.find("//li").map(&:text).should == %w(mousedown mouseup change click)
942
+ driver.find("//input[@type='checkbox']").first.set(true)
943
+ driver.find("//li").map(&:text).should == %w(mousedown focus mouseup change click)
706
944
  end
707
945
  end
708
946
 
709
947
  context "mouse app" do
710
- before(:all) do
711
- @app =lambda do |env|
712
- body = <<-HTML
713
- <html><body>
714
- <div id="change">Change me</div>
715
- <div id="mouseup">Push me</div>
716
- <div id="mousedown">Release me</div>
717
- <form action="/" method="GET">
718
- <select id="change_select" name="change_select">
719
- <option value="1" id="option-1" selected="selected">one</option>
720
- <option value="2" id="option-2">two</option>
721
- </select>
722
- </form>
723
- <script type="text/javascript">
724
- document.getElementById("change_select").
725
- addEventListener("change", function () {
726
- this.className = "triggered";
727
- });
728
- document.getElementById("change").
729
- addEventListener("change", function () {
730
- this.className = "triggered";
731
- });
732
- document.getElementById("mouseup").
733
- addEventListener("mouseup", function () {
734
- this.className = "triggered";
735
- });
736
- document.getElementById("mousedown").
737
- addEventListener("mousedown", function () {
738
- this.className = "triggered";
739
- });
740
- </script>
741
- <a href="/next">Next</a>
742
- </body></html>
743
- HTML
744
- [200,
745
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
746
- [body]]
747
- end
748
- end
948
+ let(:driver) do
949
+ driver_for_html(<<-HTML)
950
+ <html><body>
951
+ <div id="change">Change me</div>
952
+ <div id="mouseup">Push me</div>
953
+ <div id="mousedown">Release me</div>
954
+ <form action="/" method="GET">
955
+ <select id="change_select" name="change_select">
956
+ <option value="1" id="option-1" selected="selected">one</option>
957
+ <option value="2" id="option-2">two</option>
958
+ </select>
959
+ </form>
960
+ <script type="text/javascript">
961
+ document.getElementById("change_select").
962
+ addEventListener("change", function () {
963
+ this.className = "triggered";
964
+ });
965
+ document.getElementById("change").
966
+ addEventListener("change", function () {
967
+ this.className = "triggered";
968
+ });
969
+ document.getElementById("mouseup").
970
+ addEventListener("mouseup", function () {
971
+ this.className = "triggered";
972
+ });
973
+ document.getElementById("mousedown").
974
+ addEventListener("mousedown", function () {
975
+ this.className = "triggered";
976
+ });
977
+ </script>
978
+ <a href="/next">Next</a>
979
+ </body></html>
980
+ HTML
981
+ end
982
+
983
+ before { driver.visit("/") }
749
984
 
750
985
  it "clicks an element" do
751
- subject.find("//a").first.click
752
- subject.current_url =~ %r{/next$}
986
+ driver.find("//a").first.click
987
+ driver.current_url =~ %r{/next$}
753
988
  end
754
989
 
755
990
  it "fires a mouse event" do
756
- subject.find("//*[@id='mouseup']").first.trigger("mouseup")
757
- subject.find("//*[@class='triggered']").should_not be_empty
991
+ driver.find("//*[@id='mouseup']").first.trigger("mouseup")
992
+ driver.find("//*[@class='triggered']").should_not be_empty
758
993
  end
759
994
 
760
995
  it "fires a non-mouse event" do
761
- subject.find("//*[@id='change']").first.trigger("change")
762
- subject.find("//*[@class='triggered']").should_not be_empty
996
+ driver.find("//*[@id='change']").first.trigger("change")
997
+ driver.find("//*[@class='triggered']").should_not be_empty
763
998
  end
764
999
 
765
1000
  it "fires a change on select" do
766
- select = subject.find("//select").first
1001
+ select = driver.find("//select").first
767
1002
  select.value.should == "1"
768
- option = subject.find("//option[@id='option-2']").first
1003
+ option = driver.find("//option[@id='option-2']").first
769
1004
  option.select_option
770
1005
  select.value.should == "2"
771
- subject.find("//select[@class='triggered']").should_not be_empty
1006
+ driver.find("//select[@class='triggered']").should_not be_empty
772
1007
  end
773
1008
 
774
1009
  it "fires drag events" do
775
- draggable = subject.find("//*[@id='mousedown']").first
776
- container = subject.find("//*[@id='mouseup']").first
1010
+ draggable = driver.find("//*[@id='mousedown']").first
1011
+ container = driver.find("//*[@id='mouseup']").first
777
1012
 
778
1013
  draggable.drag_to(container)
779
1014
 
780
- subject.find("//*[@class='triggered']").size.should == 1
1015
+ driver.find("//*[@class='triggered']").size.should == 1
781
1016
  end
782
1017
  end
783
1018
 
784
1019
  context "nesting app" do
785
- before(:all) do
786
- @app = lambda do |env|
787
- body = <<-HTML
788
- <html><body>
789
- <div id="parent">
790
- <div class="find">Expected</div>
791
- </div>
792
- <div class="find">Unexpected</div>
793
- </body></html>
794
- HTML
795
- [200,
796
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
797
- [body]]
798
- end
1020
+ let(:driver) do
1021
+ driver_for_html(<<-HTML)
1022
+ <html><body>
1023
+ <div id="parent">
1024
+ <div class="find">Expected</div>
1025
+ </div>
1026
+ <div class="find">Unexpected</div>
1027
+ </body></html>
1028
+ HTML
799
1029
  end
800
1030
 
1031
+ before { driver.visit("/") }
1032
+
801
1033
  it "evaluates nested xpath expressions" do
802
- parent = subject.find("//*[@id='parent']").first
1034
+ parent = driver.find("//*[@id='parent']").first
803
1035
  parent.find("./*[@class='find']").map(&:text).should == %w(Expected)
804
1036
  end
805
1037
  end
806
1038
 
807
1039
  context "slow app" do
808
- before(:all) do
809
- @result = ""
810
- @app = lambda do |env|
811
- if env["PATH_INFO"] == "/result"
1040
+ it "waits for a request to load" do
1041
+ result = ""
1042
+ driver = driver_for_app do
1043
+ get "/result" do
812
1044
  sleep(0.5)
813
- @result << "finished"
1045
+ result << "finished"
1046
+ ""
814
1047
  end
815
- body = %{<html><body><a href="/result">Go</a></body></html>}
816
- [200,
817
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
818
- [body]]
819
- end
820
- end
821
1048
 
822
- it "waits for a request to load" do
823
- subject.find("//a").first.click
824
- @result.should == "finished"
1049
+ get "/" do
1050
+ %{<html><body><a href="/result">Go</a></body></html>}
1051
+ end
1052
+ end
1053
+ driver.visit("/")
1054
+ driver.find("//a").first.click
1055
+ result.should == "finished"
825
1056
  end
826
1057
  end
827
1058
 
828
1059
  context "error app" do
829
- before(:all) do
830
- @app = lambda do |env|
831
- if env['PATH_INFO'] == "/error"
832
- [404, {}, []]
833
- else
834
- body = <<-HTML
1060
+ let(:driver) do
1061
+ driver_for_app do
1062
+ get "/error" do
1063
+ invalid_response
1064
+ end
1065
+
1066
+ get "/" do
1067
+ <<-HTML
835
1068
  <html><body>
836
1069
  <form action="/error"><input type="submit"/></form>
837
1070
  </body></html>
838
1071
  HTML
839
- [200,
840
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
841
- [body]]
842
1072
  end
843
1073
  end
844
1074
  end
845
1075
 
1076
+ before { driver.visit("/") }
1077
+
846
1078
  it "raises a webkit error for the requested url" do
847
1079
  expect {
848
- subject.find("//input").first.click
1080
+ driver.find("//input").first.click
849
1081
  wait_for_error_to_complete
850
- subject.find("//body")
1082
+ driver.find("//body")
851
1083
  }.
852
- to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError, %r{/error})
1084
+ to raise_error(Capybara::Webkit::InvalidResponseError, %r{/error})
853
1085
  end
854
1086
 
855
1087
  def wait_for_error_to_complete
@@ -858,171 +1090,167 @@ describe Capybara::Driver::Webkit do
858
1090
  end
859
1091
 
860
1092
  context "slow error app" do
861
- before(:all) do
862
- @app = lambda do |env|
863
- if env['PATH_INFO'] == "/error"
864
- body = "error"
1093
+ let(:driver) do
1094
+ driver_for_app do
1095
+ get "/error" do
865
1096
  sleep(1)
866
- [304, {}, []]
867
- else
868
- body = <<-HTML
1097
+ invalid_response
1098
+ end
1099
+
1100
+ get "/" do
1101
+ <<-HTML
869
1102
  <html><body>
870
1103
  <form action="/error"><input type="submit"/></form>
871
1104
  <p>hello</p>
872
1105
  </body></html>
873
1106
  HTML
874
- [200,
875
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
876
- [body]]
877
1107
  end
878
1108
  end
879
1109
  end
880
1110
 
1111
+ before { driver.visit("/") }
1112
+
881
1113
  it "raises a webkit error and then continues" do
882
- subject.find("//input").first.click
883
- expect { subject.find("//p") }.to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError)
884
- subject.visit("/")
885
- subject.find("//p").first.text.should == "hello"
1114
+ driver.find("//input").first.click
1115
+ expect { driver.find("//p") }.to raise_error(Capybara::Webkit::InvalidResponseError)
1116
+ driver.visit("/")
1117
+ driver.find("//p").first.text.should == "hello"
886
1118
  end
887
1119
  end
888
1120
 
889
1121
  context "popup app" do
890
- before(:all) do
891
- @app = lambda do |env|
892
- body = <<-HTML
893
- <html><body>
894
- <script type="text/javascript">
895
- alert("alert");
896
- confirm("confirm");
897
- prompt("prompt");
898
- </script>
899
- <p>success</p>
900
- </body></html>
901
- HTML
902
- sleep(0.5)
903
- [200,
904
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
905
- [body]]
1122
+ let(:driver) do
1123
+ driver_for_app do
1124
+ get "/" do
1125
+ sleep(0.5)
1126
+ return <<-HTML
1127
+ <html><body>
1128
+ <script type="text/javascript">
1129
+ alert("alert");
1130
+ confirm("confirm");
1131
+ prompt("prompt");
1132
+ </script>
1133
+ <p>success</p>
1134
+ </body></html>
1135
+ HTML
1136
+ end
906
1137
  end
907
1138
  end
908
1139
 
1140
+ before { driver.visit("/") }
1141
+
909
1142
  it "doesn't crash from alerts" do
910
- subject.find("//p").first.text.should == "success"
1143
+ driver.find("//p").first.text.should == "success"
911
1144
  end
912
1145
  end
913
1146
 
914
1147
  context "custom header" do
915
- before(:all) do
916
- @app = lambda do |env|
917
- body = <<-HTML
918
- <html><body>
919
- <p id="user-agent">#{env['HTTP_USER_AGENT']}</p>
920
- <p id="x-capybara-webkit-header">#{env['HTTP_X_CAPYBARA_WEBKIT_HEADER']}</p>
921
- <p id="accept">#{env['HTTP_ACCEPT']}</p>
922
- <a href="/">/</a>
923
- </body></html>
924
- HTML
925
- [200,
926
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
927
- [body]]
1148
+ let(:driver) do
1149
+ driver_for_app do
1150
+ get "/" do
1151
+ <<-HTML
1152
+ <html><body>
1153
+ <p id="user-agent">#{env['HTTP_USER_AGENT']}</p>
1154
+ <p id="x-capybara-webkit-header">#{env['HTTP_X_CAPYBARA_WEBKIT_HEADER']}</p>
1155
+ <p id="accept">#{env['HTTP_ACCEPT']}</p>
1156
+ <a href="/">/</a>
1157
+ </body></html>
1158
+ HTML
1159
+ end
928
1160
  end
929
1161
  end
930
1162
 
1163
+ before { driver.visit("/") }
1164
+
931
1165
  before do
932
- subject.header('user-agent', 'capybara-webkit/custom-user-agent')
933
- subject.header('x-capybara-webkit-header', 'x-capybara-webkit-header')
934
- subject.header('accept', 'text/html')
935
- subject.visit('/')
1166
+ driver.header('user-agent', 'capybara-webkit/custom-user-agent')
1167
+ driver.header('x-capybara-webkit-header', 'x-capybara-webkit-header')
1168
+ driver.header('accept', 'text/html')
1169
+ driver.visit('/')
936
1170
  end
937
1171
 
938
1172
  it "can set user_agent" do
939
- subject.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent'
940
- subject.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent'
1173
+ driver.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent'
1174
+ driver.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent'
941
1175
  end
942
1176
 
943
1177
  it "keep user_agent in next page" do
944
- subject.find("//a").first.click
945
- subject.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent'
946
- subject.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent'
1178
+ driver.find("//a").first.click
1179
+ driver.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent'
1180
+ driver.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent'
947
1181
  end
948
1182
 
949
1183
  it "can set custom header" do
950
- subject.find('id("x-capybara-webkit-header")').first.text.should == 'x-capybara-webkit-header'
1184
+ driver.find('id("x-capybara-webkit-header")').first.text.should == 'x-capybara-webkit-header'
951
1185
  end
952
1186
 
953
1187
  it "can set Accept header" do
954
- subject.find('id("accept")').first.text.should == 'text/html'
1188
+ driver.find('id("accept")').first.text.should == 'text/html'
955
1189
  end
956
1190
 
957
1191
  it "can reset all custom header" do
958
- subject.reset!
959
- subject.visit('/')
960
- subject.find('id("user-agent")').first.text.should_not == 'capybara-webkit/custom-user-agent'
961
- subject.evaluate_script('navigator.userAgent').should_not == 'capybara-webkit/custom-user-agent'
962
- subject.find('id("x-capybara-webkit-header")').first.text.should be_empty
963
- subject.find('id("accept")').first.text.should_not == 'text/html'
1192
+ driver.reset!
1193
+ driver.visit('/')
1194
+ driver.find('id("user-agent")').first.text.should_not == 'capybara-webkit/custom-user-agent'
1195
+ driver.evaluate_script('navigator.userAgent').should_not == 'capybara-webkit/custom-user-agent'
1196
+ driver.find('id("x-capybara-webkit-header")').first.text.should be_empty
1197
+ driver.find('id("accept")').first.text.should_not == 'text/html'
964
1198
  end
965
1199
  end
966
1200
 
967
1201
  context "no response app" do
968
- before(:all) do
969
- @app = lambda do |env|
970
- body = <<-HTML
971
- <html><body>
972
- <form action="/error"><input type="submit"/></form>
973
- </body></html>
974
- HTML
975
- [200,
976
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
977
- [body]]
978
- end
1202
+ let(:driver) do
1203
+ driver_for_html(<<-HTML)
1204
+ <html><body>
1205
+ <form action="/error"><input type="submit"/></form>
1206
+ </body></html>
1207
+ HTML
979
1208
  end
980
1209
 
1210
+ before { driver.visit("/") }
1211
+
981
1212
  it "raises a webkit error for the requested url" do
982
1213
  make_the_server_go_away
983
1214
  expect {
984
- subject.find("//body")
1215
+ driver.find("//body")
985
1216
  }.
986
- to raise_error(Capybara::Driver::Webkit::WebkitNoResponseError, %r{response})
1217
+ to raise_error(Capybara::Webkit::NoResponseError, %r{response})
987
1218
  make_the_server_come_back
988
1219
  end
989
1220
 
990
1221
  def make_the_server_come_back
991
- subject.browser.instance_variable_get(:@connection).unstub!(:gets)
992
- subject.browser.instance_variable_get(:@connection).unstub!(:puts)
993
- subject.browser.instance_variable_get(:@connection).unstub!(:print)
1222
+ driver.browser.instance_variable_get(:@connection).unstub!(:gets)
1223
+ driver.browser.instance_variable_get(:@connection).unstub!(:puts)
1224
+ driver.browser.instance_variable_get(:@connection).unstub!(:print)
994
1225
  end
995
1226
 
996
1227
  def make_the_server_go_away
997
- subject.browser.instance_variable_get(:@connection).stub!(:gets).and_return(nil)
998
- subject.browser.instance_variable_get(:@connection).stub!(:puts)
999
- subject.browser.instance_variable_get(:@connection).stub!(:print)
1228
+ driver.browser.instance_variable_get(:@connection).stub!(:gets).and_return(nil)
1229
+ driver.browser.instance_variable_get(:@connection).stub!(:puts)
1230
+ driver.browser.instance_variable_get(:@connection).stub!(:print)
1000
1231
  end
1001
1232
  end
1002
1233
 
1003
1234
  context "custom font app" do
1004
- before(:all) do
1005
- @app = lambda do |env|
1006
- body = <<-HTML
1007
- <html>
1008
- <head>
1009
- <style type="text/css">
1010
- p { font-family: "Verdana"; }
1011
- </style>
1012
- </head>
1013
- <body>
1014
- <p id="text">Hello</p>
1015
- </body>
1016
- </html>
1017
- HTML
1018
- [200,
1019
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1020
- [body]]
1021
- end
1235
+ let(:driver) do
1236
+ driver_for_html(<<-HTML)
1237
+ <html>
1238
+ <head>
1239
+ <style type="text/css">
1240
+ p { font-family: "Verdana"; }
1241
+ </style>
1242
+ </head>
1243
+ <body>
1244
+ <p id="text">Hello</p>
1245
+ </body>
1246
+ </html>
1247
+ HTML
1022
1248
  end
1023
1249
 
1250
+ before { driver.visit("/") }
1251
+
1024
1252
  it "ignores custom fonts" do
1025
- font_family = subject.evaluate_script(<<-SCRIPT)
1253
+ font_family = driver.evaluate_script(<<-SCRIPT)
1026
1254
  var element = document.getElementById("text");
1027
1255
  element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue("font-family");
1028
1256
  SCRIPT
@@ -1031,49 +1259,45 @@ describe Capybara::Driver::Webkit do
1031
1259
  end
1032
1260
 
1033
1261
  context "cookie-based app" do
1034
- before(:all) do
1035
- @cookie = 'cookie=abc; domain=127.0.0.1; path=/'
1036
- @app = lambda do |env|
1037
- request = ::Rack::Request.new(env)
1038
-
1039
- body = <<-HTML
1040
- <html><body>
1041
- <p id="cookie">#{request.cookies["cookie"] || ""}</p>
1042
- </body></html>
1043
- HTML
1044
- [200,
1045
- { 'Content-Type' => 'text/html; charset=UTF-8',
1046
- 'Content-Length' => body.length.to_s,
1047
- 'Set-Cookie' => @cookie,
1048
- },
1049
- [body]]
1262
+ let(:driver) do
1263
+ driver_for_app do
1264
+ get "/" do
1265
+ headers 'Set-Cookie' => 'cookie=abc; domain=127.0.0.1; path=/'
1266
+ <<-HTML
1267
+ <html><body>
1268
+ <p id="cookie">#{request.cookies["cookie"] || ""}</p>
1269
+ </body></html>
1270
+ HTML
1271
+ end
1050
1272
  end
1051
1273
  end
1052
1274
 
1275
+ before { driver.visit("/") }
1276
+
1053
1277
  def echoed_cookie
1054
- subject.find('id("cookie")').first.text
1278
+ driver.find('id("cookie")').first.text
1055
1279
  end
1056
1280
 
1057
1281
  it "remembers the cookie on second visit" do
1058
1282
  echoed_cookie.should == ""
1059
- subject.visit "/"
1283
+ driver.visit "/"
1060
1284
  echoed_cookie.should == "abc"
1061
1285
  end
1062
1286
 
1063
1287
  it "uses a custom cookie" do
1064
- subject.browser.set_cookie @cookie
1065
- subject.visit "/"
1288
+ driver.browser.set_cookie 'cookie=abc; domain=127.0.0.1; path=/'
1289
+ driver.visit "/"
1066
1290
  echoed_cookie.should == "abc"
1067
1291
  end
1068
1292
 
1069
1293
  it "clears cookies" do
1070
- subject.browser.clear_cookies
1071
- subject.visit "/"
1294
+ driver.browser.clear_cookies
1295
+ driver.visit "/"
1072
1296
  echoed_cookie.should == ""
1073
1297
  end
1074
1298
 
1075
1299
  it "allows enumeration of cookies" do
1076
- cookies = subject.browser.get_cookies
1300
+ cookies = driver.browser.get_cookies
1077
1301
 
1078
1302
  cookies.size.should == 1
1079
1303
 
@@ -1084,64 +1308,23 @@ describe Capybara::Driver::Webkit do
1084
1308
  end
1085
1309
 
1086
1310
  it "allows reading access to cookies using a nice syntax" do
1087
- subject.cookies["cookie"].should == "abc"
1088
- end
1089
- end
1090
-
1091
- context "with socket debugger" do
1092
- let(:socket_debugger_class){ Capybara::Driver::Webkit::SocketDebugger }
1093
- let(:browser_with_debugger){
1094
- connection = Capybara::Driver::Webkit::Connection.new(:socket_class => socket_debugger_class)
1095
- Capybara::Driver::Webkit::Browser.new(connection)
1096
- }
1097
- let(:driver_with_debugger){ Capybara::Driver::Webkit.new(@app, :browser => browser_with_debugger) }
1098
-
1099
- before(:all) do
1100
- @app = lambda do |env|
1101
- body = <<-HTML
1102
- <html><body>
1103
- <div id="parent">
1104
- <div class="find">Expected</div>
1105
- </div>
1106
- <div class="find">Unexpected</div>
1107
- </body></html>
1108
- HTML
1109
- [200,
1110
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1111
- [body]]
1112
- end
1113
- end
1114
-
1115
- it "prints out sent content" do
1116
- socket_debugger_class.any_instance.stub(:received){|content| content }
1117
- sent_content = ['Find', 1, 17, "//*[@id='parent']"]
1118
- socket_debugger_class.any_instance.should_receive(:sent).exactly(sent_content.size).times
1119
- driver_with_debugger.find("//*[@id='parent']")
1120
- end
1121
-
1122
- it "prints out received content" do
1123
- socket_debugger_class.any_instance.stub(:sent)
1124
- socket_debugger_class.any_instance.should_receive(:received).at_least(:once).and_return("ok")
1125
- driver_with_debugger.find("//*[@id='parent']")
1311
+ driver.cookies["cookie"].should == "abc"
1126
1312
  end
1127
1313
  end
1128
1314
 
1129
1315
  context "remove node app" do
1130
- before(:all) do
1131
- @app = lambda do |env|
1132
- body = <<-HTML
1133
- <html>
1134
- <div id="parent">
1135
- <p id="removeMe">Hello</p>
1136
- </div>
1137
- </html>
1138
- HTML
1139
- [200,
1140
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1141
- [body]]
1142
- end
1316
+ let(:driver) do
1317
+ driver_for_html(<<-HTML)
1318
+ <html>
1319
+ <div id="parent">
1320
+ <p id="removeMe">Hello</p>
1321
+ </div>
1322
+ </html>
1323
+ HTML
1143
1324
  end
1144
1325
 
1326
+ before { driver.visit("/") }
1327
+
1145
1328
  before { set_automatic_reload false }
1146
1329
  after { set_automatic_reload true }
1147
1330
 
@@ -1152,58 +1335,55 @@ describe Capybara::Driver::Webkit do
1152
1335
  end
1153
1336
 
1154
1337
  it "allows removed nodes when reloading is disabled" do
1155
- node = subject.find("//p[@id='removeMe']").first
1156
- subject.evaluate_script("document.getElementById('parent').innerHTML = 'Magic'")
1338
+ node = driver.find("//p[@id='removeMe']").first
1339
+ driver.evaluate_script("document.getElementById('parent').innerHTML = 'Magic'")
1157
1340
  node.text.should == 'Hello'
1158
1341
  end
1159
1342
  end
1160
1343
 
1161
1344
  context "app with a lot of HTML tags" do
1162
- before(:all) do
1163
- @app = lambda do |env|
1164
- body = <<-HTML
1165
- <html>
1166
- <head>
1167
- <title>My eBook</title>
1168
- <meta class="charset" name="charset" value="utf-8" />
1169
- <meta class="author" name="author" value="Firstname Lastname" />
1170
- </head>
1171
- <body>
1172
- <div id="toc">
1173
- <table>
1174
- <thead id="head">
1175
- <tr><td class="td1">Chapter</td><td>Page</td></tr>
1176
- </thead>
1177
- <tbody>
1178
- <tr><td>Intro</td><td>1</td></tr>
1179
- <tr><td>Chapter 1</td><td class="td2">1</td></tr>
1180
- <tr><td>Chapter 2</td><td>1</td></tr>
1181
- </tbody>
1182
- </table>
1183
- </div>
1184
-
1185
- <h1 class="h1">My first book</h1>
1186
- <p class="p1">Written by me</p>
1187
- <div id="intro" class="intro">
1188
- <p>Let's try out XPath</p>
1189
- <p class="p2">in capybara-webkit</p>
1190
- </div>
1191
-
1192
- <h2 class="chapter1">Chapter 1</h2>
1193
- <p>This paragraph is fascinating.</p>
1194
- <p class="p3">But not as much as this one.</p>
1195
-
1196
- <h2 class="chapter2">Chapter 2</h2>
1197
- <p>Let's try if we can select this</p>
1198
- </body>
1199
- </html>
1200
- HTML
1201
- [200,
1202
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1203
- [body]]
1204
- end
1345
+ let(:driver) do
1346
+ driver_for_html(<<-HTML)
1347
+ <html>
1348
+ <head>
1349
+ <title>My eBook</title>
1350
+ <meta class="charset" name="charset" value="utf-8" />
1351
+ <meta class="author" name="author" value="Firstname Lastname" />
1352
+ </head>
1353
+ <body>
1354
+ <div id="toc">
1355
+ <table>
1356
+ <thead id="head">
1357
+ <tr><td class="td1">Chapter</td><td>Page</td></tr>
1358
+ </thead>
1359
+ <tbody>
1360
+ <tr><td>Intro</td><td>1</td></tr>
1361
+ <tr><td>Chapter 1</td><td class="td2">1</td></tr>
1362
+ <tr><td>Chapter 2</td><td>1</td></tr>
1363
+ </tbody>
1364
+ </table>
1365
+ </div>
1366
+
1367
+ <h1 class="h1">My first book</h1>
1368
+ <p class="p1">Written by me</p>
1369
+ <div id="intro" class="intro">
1370
+ <p>Let's try out XPath</p>
1371
+ <p class="p2">in capybara-webkit</p>
1372
+ </div>
1373
+
1374
+ <h2 class="chapter1">Chapter 1</h2>
1375
+ <p>This paragraph is fascinating.</p>
1376
+ <p class="p3">But not as much as this one.</p>
1377
+
1378
+ <h2 class="chapter2">Chapter 2</h2>
1379
+ <p>Let's try if we can select this</p>
1380
+ </body>
1381
+ </html>
1382
+ HTML
1205
1383
  end
1206
1384
 
1385
+ before { driver.visit("/") }
1386
+
1207
1387
  it "builds up node paths correctly" do
1208
1388
  cases = {
1209
1389
  "//*[contains(@class, 'author')]" => "/html/head/meta[2]",
@@ -1217,7 +1397,7 @@ describe Capybara::Driver::Webkit do
1217
1397
  }
1218
1398
 
1219
1399
  cases.each do |xpath, path|
1220
- nodes = subject.find(xpath)
1400
+ nodes = driver.find(xpath)
1221
1401
  nodes.size.should == 1
1222
1402
  nodes[0].path.should == path
1223
1403
  end
@@ -1225,165 +1405,94 @@ describe Capybara::Driver::Webkit do
1225
1405
  end
1226
1406
 
1227
1407
  context "css overflow app" do
1228
- before(:all) do
1229
- @app = lambda do |env|
1230
- body = <<-HTML
1231
- <html>
1232
- <head>
1233
- <style type="text/css">
1234
- #overflow { overflow: hidden }
1235
- </style>
1236
- </head>
1237
- <body>
1238
- <div id="overflow">Overflow</div>
1239
- </body>
1240
- </html>
1241
- HTML
1242
- [200,
1243
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1244
- [body]]
1245
- end
1408
+ let(:driver) do
1409
+ driver_for_html(<<-HTML)
1410
+ <html>
1411
+ <head>
1412
+ <style type="text/css">
1413
+ #overflow { overflow: hidden }
1414
+ </style>
1415
+ </head>
1416
+ <body>
1417
+ <div id="overflow">Overflow</div>
1418
+ </body>
1419
+ </html>
1420
+ HTML
1246
1421
  end
1247
1422
 
1423
+ before { driver.visit("/") }
1424
+
1248
1425
  it "handles overflow hidden" do
1249
- subject.find("//div[@id='overflow']").first.text.should == "Overflow"
1426
+ driver.find("//div[@id='overflow']").first.text.should == "Overflow"
1250
1427
  end
1251
1428
  end
1252
1429
 
1253
1430
  context "javascript redirect app" do
1254
- before(:all) do
1255
- @app = lambda do |env|
1256
- if env['PATH_INFO'] == '/redirect'
1257
- body = <<-HTML
1431
+ let(:driver) do
1432
+ driver_for_app do
1433
+ get '/redirect' do
1434
+ <<-HTML
1258
1435
  <html>
1259
1436
  <script type="text/javascript">
1260
- window.location = "/next";
1437
+ window.location = "/";
1261
1438
  </script>
1262
1439
  </html>
1263
1440
  HTML
1264
- else
1265
- body = "<html><p>finished</p></html>"
1266
1441
  end
1267
- [200,
1268
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1269
- [body]]
1442
+
1443
+ get '/' do
1444
+ "<html><p>finished</p></html>"
1445
+ end
1270
1446
  end
1271
1447
  end
1272
1448
 
1273
1449
  it "loads a page without error" do
1274
1450
  10.times do
1275
- subject.visit("/redirect")
1276
- subject.find("//p").first.text.should == "finished"
1451
+ driver.visit("/redirect")
1452
+ driver.find("//p").first.text.should == "finished"
1277
1453
  end
1278
1454
  end
1279
1455
  end
1280
1456
 
1281
1457
  context "localStorage works" do
1282
- before(:all) do
1283
- @app = lambda do |env|
1284
- body = <<-HTML
1285
- <html>
1286
- <body>
1287
- <span id='output'></span>
1288
- <script type="text/javascript">
1289
- if (typeof localStorage !== "undefined") {
1290
- if (!localStorage.refreshCounter) {
1291
- localStorage.refreshCounter = 0;
1292
- }
1293
- if (localStorage.refreshCounter++ > 0) {
1294
- document.getElementById("output").innerHTML = "localStorage is enabled";
1295
- }
1458
+ let(:driver) do
1459
+ driver_for_html(<<-HTML)
1460
+ <html>
1461
+ <body>
1462
+ <span id='output'></span>
1463
+ <script type="text/javascript">
1464
+ if (typeof localStorage !== "undefined") {
1465
+ if (!localStorage.refreshCounter) {
1466
+ localStorage.refreshCounter = 0;
1296
1467
  }
1297
- </script>
1298
- </body>
1299
- </html>
1300
- HTML
1301
- [200,
1302
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1303
- [body]]
1304
- end
1305
- end
1306
-
1307
- it "displays the message on subsequent page loads" do
1308
- subject.find("//span[contains(.,'localStorage is enabled')]").should be_empty
1309
- subject.visit "/"
1310
- subject.find("//span[contains(.,'localStorage is enabled')]").should_not be_empty
1311
- end
1312
- end
1313
-
1314
- context "app with a lot of HTML tags" do
1315
- before(:all) do
1316
- @app = lambda do |env|
1317
- body = <<-HTML
1318
- <html>
1319
- <head>
1320
- <title>My eBook</title>
1321
- <meta class="charset" name="charset" value="utf-8" />
1322
- <meta class="author" name="author" value="Firstname Lastname" />
1323
- </head>
1324
- <body>
1325
- <div id="toc">
1326
- <table>
1327
- <thead id="head">
1328
- <tr><td class="td1">Chapter</td><td>Page</td></tr>
1329
- </thead>
1330
- <tbody>
1331
- <tr><td>Intro</td><td>1</td></tr>
1332
- <tr><td>Chapter 1</td><td class="td2">1</td></tr>
1333
- <tr><td>Chapter 2</td><td>1</td></tr>
1334
- </tbody>
1335
- </table>
1336
- </div>
1337
-
1338
- <h1 class="h1">My first book</h1>
1339
- <p class="p1">Written by me</p>
1340
- <div id="intro" class="intro">
1341
- <p>Let's try out XPath</p>
1342
- <p class="p2">in capybara-webkit</p>
1343
- </div>
1344
-
1345
- <h2 class="chapter1">Chapter 1</h2>
1346
- <p>This paragraph is fascinating.</p>
1347
- <p class="p3">But not as much as this one.</p>
1348
-
1349
- <h2 class="chapter2">Chapter 2</h2>
1350
- <p>Let's try if we can select this</p>
1351
- </body>
1352
- </html>
1353
- HTML
1354
- [200,
1355
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1356
- [body]]
1357
- end
1468
+ if (localStorage.refreshCounter++ > 0) {
1469
+ document.getElementById("output").innerHTML = "localStorage is enabled";
1470
+ }
1471
+ }
1472
+ </script>
1473
+ </body>
1474
+ </html>
1475
+ HTML
1358
1476
  end
1359
1477
 
1360
- it "builds up node paths correctly" do
1361
- cases = {
1362
- "//*[contains(@class, 'author')]" => "/html/head/meta[2]",
1363
- "//*[contains(@class, 'td1')]" => "/html/body/div[@id='toc']/table/thead[@id='head']/tr/td[1]",
1364
- "//*[contains(@class, 'td2')]" => "/html/body/div[@id='toc']/table/tbody/tr[2]/td[2]",
1365
- "//h1" => "/html/body/h1",
1366
- "//*[contains(@class, 'chapter2')]" => "/html/body/h2[2]",
1367
- "//*[contains(@class, 'p1')]" => "/html/body/p[1]",
1368
- "//*[contains(@class, 'p2')]" => "/html/body/div[@id='intro']/p[2]",
1369
- "//*[contains(@class, 'p3')]" => "/html/body/p[3]",
1370
- }
1478
+ before { driver.visit("/") }
1371
1479
 
1372
- cases.each do |xpath, path|
1373
- nodes = subject.find(xpath)
1374
- nodes.size.should == 1
1375
- nodes[0].path.should == path
1376
- end
1480
+ it "displays the message on subsequent page loads" do
1481
+ driver.find("//span[contains(.,'localStorage is enabled')]").should be_empty
1482
+ driver.visit "/"
1483
+ driver.find("//span[contains(.,'localStorage is enabled')]").should_not be_empty
1377
1484
  end
1378
1485
  end
1379
1486
 
1380
1487
  context "form app with server-side handler" do
1381
- before(:all) do
1382
- @app = lambda do |env|
1383
- if env["REQUEST_METHOD"] == "POST"
1384
- body = "<html><body><p>Congrats!</p></body></html>"
1385
- else
1386
- body = <<-HTML
1488
+ let(:driver) do
1489
+ driver_for_app do
1490
+ post "/" do
1491
+ "<html><body><p>Congrats!</p></body></html>"
1492
+ end
1493
+
1494
+ get "/" do
1495
+ <<-HTML
1387
1496
  <html>
1388
1497
  <head><title>Form</title>
1389
1498
  <body>
@@ -1395,67 +1504,66 @@ describe Capybara::Driver::Webkit do
1395
1504
  </html>
1396
1505
  HTML
1397
1506
  end
1398
- [200,
1399
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1400
- [body]]
1401
1507
  end
1402
1508
  end
1403
1509
 
1510
+ before { driver.visit("/") }
1511
+
1404
1512
  it "submits a form without clicking" do
1405
- subject.find("//form")[0].submit
1406
- subject.body.should include "Congrats"
1513
+ driver.find("//form")[0].submit
1514
+ driver.body.should include "Congrats"
1407
1515
  end
1408
1516
  end
1409
1517
 
1410
- def key_app_body(event)
1411
- body = <<-HTML
1412
- <html>
1413
- <head><title>Form</title></head>
1414
- <body>
1415
- <div id="charcode_value"></div>
1416
- <div id="keycode_value"></div>
1417
- <div id="which_value"></div>
1418
- <input type="text" id="charcode" name="charcode" on#{event}="setcharcode" />
1419
- <script type="text/javascript">
1420
- var element = document.getElementById("charcode")
1421
- element.addEventListener("#{event}", setcharcode);
1422
- function setcharcode(event) {
1423
- var element = document.getElementById("charcode_value");
1424
- element.innerHTML = event.charCode;
1425
- element = document.getElementById("keycode_value");
1426
- element.innerHTML = event.keyCode;
1427
- element = document.getElementById("which_value");
1428
- element.innerHTML = event.which;
1429
- }
1430
- </script>
1431
- </body>
1432
- </html>
1433
- HTML
1434
- body
1518
+ def driver_for_key_body(event)
1519
+ driver_for_app do
1520
+ get "/" do
1521
+ <<-HTML
1522
+ <html>
1523
+ <head><title>Form</title></head>
1524
+ <body>
1525
+ <div id="charcode_value"></div>
1526
+ <div id="keycode_value"></div>
1527
+ <div id="which_value"></div>
1528
+ <input type="text" id="charcode" name="charcode" on#{event}="setcharcode" />
1529
+ <script type="text/javascript">
1530
+ var element = document.getElementById("charcode")
1531
+ element.addEventListener("#{event}", setcharcode);
1532
+ function setcharcode(event) {
1533
+ var element = document.getElementById("charcode_value");
1534
+ element.innerHTML = event.charCode;
1535
+ element = document.getElementById("keycode_value");
1536
+ element.innerHTML = event.keyCode;
1537
+ element = document.getElementById("which_value");
1538
+ element.innerHTML = event.which;
1539
+ }
1540
+ </script>
1541
+ </body>
1542
+ </html>
1543
+ HTML
1544
+ end
1545
+ end
1435
1546
  end
1436
1547
 
1437
1548
  def charCode_for(character)
1438
- subject.find("//input")[0].set(character)
1439
- subject.find("//div[@id='charcode_value']")[0].text
1549
+ driver.find("//input")[0].set(character)
1550
+ driver.find("//div[@id='charcode_value']")[0].text
1440
1551
  end
1441
1552
 
1442
1553
  def keyCode_for(character)
1443
- subject.find("//input")[0].set(character)
1444
- subject.find("//div[@id='keycode_value']")[0].text
1554
+ driver.find("//input")[0].set(character)
1555
+ driver.find("//div[@id='keycode_value']")[0].text
1445
1556
  end
1446
1557
 
1447
1558
  def which_for(character)
1448
- subject.find("//input")[0].set(character)
1449
- subject.find("//div[@id='which_value']")[0].text
1559
+ driver.find("//input")[0].set(character)
1560
+ driver.find("//div[@id='which_value']")[0].text
1450
1561
  end
1451
1562
 
1452
1563
  context "keypress app" do
1453
- before(:all) do
1454
- @app = lambda do |env|
1455
- body = key_app_body("keypress")
1456
- [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s }, [body]]
1457
- end
1458
- end
1564
+ let(:driver) { driver_for_key_body "keypress" }
1565
+
1566
+ before { driver.visit("/") }
1459
1567
 
1460
1568
  it "returns the charCode for the keypressed" do
1461
1569
  charCode_for("a").should == "97"
@@ -1515,38 +1623,308 @@ describe Capybara::Driver::Webkit do
1515
1623
  end
1516
1624
 
1517
1625
  context "keydown app" do
1518
- before(:all) do
1519
- @app = lambda do |env|
1520
- body = key_app_body("keydown")
1521
- [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s }, [body]]
1522
- end
1523
- end
1626
+ let(:driver) { driver_for_key_body "keydown" }
1627
+ before { driver.visit("/") }
1524
1628
  it_behaves_like "a keyupdown app"
1525
1629
  end
1526
1630
 
1527
1631
  context "keyup app" do
1528
- before(:all) do
1529
- @app = lambda do |env|
1530
- body = key_app_body("keyup")
1531
- [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s }, [body]]
1632
+ let(:driver) { driver_for_key_body "keyup" }
1633
+ before { driver.visit("/") }
1634
+ it_behaves_like "a keyupdown app"
1635
+ end
1636
+
1637
+ context "null byte app" do
1638
+ let(:driver) do
1639
+ driver_for_html("Hello\0World")
1640
+ end
1641
+
1642
+ before { driver.visit("/") }
1643
+
1644
+ it "should include all the bytes in the source" do
1645
+ driver.source.should == "Hello\0World"
1646
+ end
1647
+ end
1648
+
1649
+ context "javascript new window app" do
1650
+ let(:driver) do
1651
+ driver_for_app do
1652
+ get '/new_window' do
1653
+ <<-HTML
1654
+ <html>
1655
+ <script type="text/javascript">
1656
+ window.open('http://#{request.host_with_port}/?#{request.query_string}', 'myWindow');
1657
+ </script>
1658
+ <p>bananas</p>
1659
+ </html>
1660
+ HTML
1661
+ end
1662
+
1663
+ get "/" do
1664
+ sleep params['sleep'].to_i if params['sleep']
1665
+ "<html><head><title>My New Window</title></head><body><p>finished</p></body></html>"
1666
+ end
1532
1667
  end
1533
1668
  end
1534
1669
 
1535
- it_behaves_like "a keyupdown app"
1670
+ before { driver.visit("/") }
1671
+
1672
+ it "has the expected text in the new window" do
1673
+ driver.visit("/new_window")
1674
+ driver.within_window(driver.window_handles.last) do
1675
+ driver.find("//p").first.text.should == "finished"
1676
+ end
1677
+ end
1678
+
1679
+ it "waits for the new window to load" do
1680
+ driver.visit("/new_window?sleep=1")
1681
+ driver.within_window(driver.window_handles.last) do
1682
+ driver.find("//p").first.text.should == "finished"
1683
+ end
1684
+ end
1685
+
1686
+ it "waits for the new window to load when the window location has changed" do
1687
+ driver.visit("/new_window?sleep=2")
1688
+ driver.execute_script("setTimeout(function() { window.location = 'about:blank' }, 1000)")
1689
+ driver.within_window(driver.window_handles.last) do
1690
+ driver.find("//p").first.text.should == "finished"
1691
+ end
1692
+ end
1693
+
1694
+ it "switches back to the original window" do
1695
+ driver.visit("/new_window")
1696
+ driver.within_window(driver.window_handles.last) { }
1697
+ driver.find("//p").first.text.should == "bananas"
1698
+ end
1699
+
1700
+ it "supports finding a window by name" do
1701
+ driver.visit("/new_window")
1702
+ driver.within_window('myWindow') do
1703
+ driver.find("//p").first.text.should == "finished"
1704
+ end
1705
+ end
1706
+
1707
+ it "supports finding a window by title" do
1708
+ driver.visit("/new_window?sleep=5")
1709
+ driver.within_window('My New Window') do
1710
+ driver.find("//p").first.text.should == "finished"
1711
+ end
1712
+ end
1713
+
1714
+ it "supports finding a window by url" do
1715
+ driver.visit("/new_window?test")
1716
+ driver.within_window(driver_url(driver, "/?test")) do
1717
+ driver.find("//p").first.text.should == "finished"
1718
+ end
1719
+ end
1720
+
1721
+ it "raises an error if the window is not found" do
1722
+ expect { driver.within_window('myWindowDoesNotExist') }.
1723
+ to raise_error(Capybara::Webkit::InvalidResponseError)
1724
+ end
1725
+
1726
+ it "has a number of window handles equal to the number of open windows" do
1727
+ driver.window_handles.size.should == 1
1728
+ driver.visit("/new_window")
1729
+ driver.window_handles.size.should == 2
1730
+ end
1731
+
1732
+ it "closes new windows on reset" do
1733
+ driver.visit("/new_window")
1734
+ last_handle = driver.window_handles.last
1735
+ driver.reset!
1736
+ driver.window_handles.should_not include(last_handle)
1737
+ end
1536
1738
  end
1537
1739
 
1538
- context "null byte app" do
1539
- before(:all) do
1540
- @app = lambda do |env|
1541
- body = "Hello\0World"
1542
- [200,
1543
- { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
1544
- [body]]
1740
+ it "preserves cookies across windows" do
1741
+ session_id = '12345'
1742
+ driver = driver_for_app do
1743
+ get '/new_window' do
1744
+ <<-HTML
1745
+ <html>
1746
+ <script type="text/javascript">
1747
+ window.open('http://#{request.host_with_port}/set_cookie');
1748
+ </script>
1749
+ </html>
1750
+ HTML
1751
+ end
1752
+
1753
+ get '/set_cookie' do
1754
+ response.set_cookie 'session_id', session_id
1545
1755
  end
1546
1756
  end
1547
1757
 
1548
- it "should include all the bytes in the source" do
1549
- subject.source.should == "Hello\0World"
1758
+ driver.visit("/new_window")
1759
+ driver.cookies['session_id'].should == session_id
1760
+ end
1761
+
1762
+ context "timers app" do
1763
+ let(:driver) do
1764
+ driver_for_app do
1765
+ get "/success" do
1766
+ '<html><body></body></html>'
1767
+ end
1768
+
1769
+ get "/not-found" do
1770
+ 404
1771
+ end
1772
+
1773
+ get "/outer" do
1774
+ <<-HTML
1775
+ <html>
1776
+ <head>
1777
+ <script>
1778
+ function emit_true_load_finished(){var divTag = document.createElement("div");divTag.innerHTML = "<iframe src='/success'></iframe>";document.body.appendChild(divTag);};
1779
+ function emit_false_load_finished(){var divTag = document.createElement("div");divTag.innerHTML = "<iframe src='/not-found'></iframe>";document.body.appendChild(divTag);};
1780
+ function emit_false_true_load_finished() { emit_false_load_finished(); setTimeout('emit_true_load_finished()',100); };
1781
+ </script>
1782
+ </head>
1783
+ <body onload="setTimeout('emit_false_true_load_finished()',100)">
1784
+ </body>
1785
+ </html>
1786
+ HTML
1787
+ end
1788
+
1789
+ get '/' do
1790
+ "<html><body></body></html>"
1791
+ end
1792
+ end
1793
+ end
1794
+
1795
+ before { driver.visit("/") }
1796
+
1797
+ it "raises error for any loadFinished failure" do
1798
+ expect do
1799
+ driver.visit("/outer")
1800
+ sleep 1
1801
+ driver.find("//body")
1802
+ end.to raise_error(Capybara::Webkit::InvalidResponseError)
1803
+ end
1804
+ end
1805
+
1806
+ describe "basic auth" do
1807
+ let(:driver) do
1808
+ driver_for_app do
1809
+ get "/" do
1810
+ if env["HTTP_AUTHORIZATION"]
1811
+ env["HTTP_AUTHORIZATION"]
1812
+ else
1813
+ headers "WWW-Authenticate" => 'Basic realm="Secure Area"'
1814
+ status 401
1815
+ "401 Unauthorized."
1816
+ end
1817
+ end
1818
+ end
1819
+ end
1820
+
1821
+ it "can authenticate a request" do
1822
+ driver.browser.authenticate('user', 'password')
1823
+ driver.visit("/")
1824
+ driver.body.should include("Basic "+Base64.encode64("user:password").strip)
1550
1825
  end
1551
1826
  end
1827
+
1828
+ describe "timeout for long requests" do
1829
+ let(:driver) do
1830
+ driver_for_app do
1831
+ html = <<-HTML
1832
+ <html>
1833
+ <body>
1834
+ <form action="/form" method="post">
1835
+ <input type="submit" value="Submit"/>
1836
+ </form>
1837
+ </body>
1838
+ </html>
1839
+ HTML
1840
+
1841
+ get "/" do
1842
+ sleep(2)
1843
+ html
1844
+ end
1845
+
1846
+ post "/form" do
1847
+ sleep(4)
1848
+ html
1849
+ end
1850
+ end
1851
+ end
1852
+
1853
+ it "should not raise a timeout error when zero" do
1854
+ driver.browser.timeout = 0
1855
+ lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
1856
+ end
1857
+
1858
+ it "should raise a timeout error" do
1859
+ driver.browser.timeout = 1
1860
+ lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError, "Request timed out after 1 second")
1861
+ end
1862
+
1863
+ it "should not raise an error when the timeout is high enough" do
1864
+ driver.browser.timeout = 10
1865
+ lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
1866
+ end
1867
+
1868
+ it "should set the timeout for each request" do
1869
+ driver.browser.timeout = 10
1870
+ lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
1871
+ driver.browser.timeout = 1
1872
+ lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
1873
+ end
1874
+
1875
+ it "should set the timeout for each request" do
1876
+ driver.browser.timeout = 1
1877
+ lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
1878
+ driver.reset!
1879
+ driver.browser.timeout = 10
1880
+ lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
1881
+ end
1882
+
1883
+ it "should raise a timeout on a slow form" do
1884
+ driver.browser.timeout = 3
1885
+ driver.visit("/")
1886
+ driver.status_code.should == 200
1887
+ driver.browser.timeout = 1
1888
+ driver.find("//input").first.click
1889
+ lambda { driver.status_code }.should raise_error(Capybara::TimeoutError)
1890
+ end
1891
+
1892
+ it "get timeout" do
1893
+ driver.browser.timeout = 10
1894
+ driver.browser.timeout.should == 10
1895
+ driver.browser.timeout = 3
1896
+ driver.browser.timeout.should == 3
1897
+ end
1898
+ end
1899
+
1900
+ describe "logger app" do
1901
+ it "logs nothing before turning on the logger" do
1902
+ driver.visit("/")
1903
+ log.should == ""
1904
+ end
1905
+
1906
+ it "logs its commands after turning on the logger" do
1907
+ driver.enable_logging
1908
+ driver.visit("/")
1909
+ log.should_not == ""
1910
+ end
1911
+
1912
+ let(:driver) do
1913
+ command = "#{Capybara::Webkit::Connection::SERVER_PATH} 2>&1"
1914
+ connection = Capybara::Webkit::Connection.new(:command => command, :stdout => output)
1915
+ browser = Capybara::Webkit::Browser.new(connection)
1916
+ Capybara::Webkit::Driver.new(AppRunner.app, :browser => browser)
1917
+ end
1918
+
1919
+ let(:output) { StringIO.new }
1920
+
1921
+ def log
1922
+ output.rewind
1923
+ output.read
1924
+ end
1925
+ end
1926
+
1927
+ def driver_url(driver, path)
1928
+ URI.parse(driver.current_url).merge(path).to_s
1929
+ end
1552
1930
  end