capybara-webkit 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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