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.
- data/.gitignore +2 -1
- data/GOALS +9 -0
- data/Gemfile.lock +15 -12
- data/LICENSE +1 -1
- data/NEWS.md +18 -0
- data/README.md +81 -2
- data/capybara-webkit.gemspec +1 -1
- data/lib/capybara/webkit.rb +11 -6
- data/lib/capybara/{driver/webkit → webkit}/browser.rb +90 -4
- data/lib/capybara/{driver/webkit → webkit}/connection.rb +48 -47
- data/lib/capybara/{driver/webkit → webkit}/cookie_jar.rb +1 -1
- data/lib/capybara/webkit/driver.rb +186 -0
- data/lib/capybara/webkit/errors.rb +10 -0
- data/lib/capybara/{driver/webkit → webkit}/node.rb +12 -4
- data/lib/capybara/{driver/webkit → webkit}/socket_debugger.rb +4 -1
- data/lib/capybara/{driver/webkit → webkit}/version.rb +1 -1
- data/lib/capybara_webkit_builder.rb +17 -3
- data/spec/browser_spec.rb +95 -7
- data/spec/connection_spec.rb +16 -5
- data/spec/cookie_jar_spec.rb +3 -3
- data/spec/driver_rendering_spec.rb +19 -26
- data/spec/driver_resize_window_spec.rb +3 -3
- data/spec/driver_spec.rb +1200 -822
- data/spec/integration/driver_spec.rb +4 -3
- data/spec/selenium_compatibility_spec.rb +49 -0
- data/spec/spec_helper.rb +14 -6
- data/spec/support/app_runner.rb +94 -0
- data/src/Authenticate.cpp +18 -0
- data/src/Authenticate.h +12 -0
- data/src/Body.h +3 -5
- data/src/ClearCookies.cpp +3 -5
- data/src/ClearCookies.h +3 -5
- data/src/ClearPromptText.cpp +11 -0
- data/src/ClearPromptText.h +9 -0
- data/src/Command.cpp +4 -15
- data/src/Command.h +3 -14
- data/src/CommandFactory.cpp +20 -6
- data/src/CommandFactory.h +3 -2
- data/src/CommandParser.cpp +1 -1
- data/src/Connection.cpp +22 -21
- data/src/Connection.h +5 -6
- data/src/ConsoleMessages.cpp +2 -1
- data/src/ConsoleMessages.h +3 -5
- data/src/CurrentUrl.cpp +9 -48
- data/src/CurrentUrl.h +8 -8
- data/src/EnableLogging.cpp +10 -0
- data/src/EnableLogging.h +12 -0
- data/src/Evaluate.cpp +2 -1
- data/src/Evaluate.h +3 -5
- data/src/Execute.cpp +2 -1
- data/src/Execute.h +3 -5
- data/src/Find.cpp +3 -2
- data/src/Find.h +3 -5
- data/src/FrameFocus.cpp +3 -2
- data/src/FrameFocus.h +3 -4
- data/src/GetCookies.cpp +3 -4
- data/src/GetCookies.h +3 -5
- data/src/GetTimeout.cpp +9 -0
- data/src/GetTimeout.h +11 -0
- data/src/GetWindowHandle.cpp +11 -0
- data/src/GetWindowHandle.h +10 -0
- data/src/GetWindowHandles.cpp +20 -0
- data/src/GetWindowHandles.h +10 -0
- data/src/Header.cpp +2 -1
- data/src/Header.h +3 -5
- data/src/Headers.cpp +8 -2
- data/src/Headers.h +3 -5
- data/src/IgnoreSslErrors.cpp +4 -3
- data/src/IgnoreSslErrors.h +3 -5
- data/src/JavascriptAlertMessages.cpp +10 -0
- data/src/JavascriptAlertMessages.h +9 -0
- data/src/JavascriptConfirmMessages.cpp +10 -0
- data/src/JavascriptConfirmMessages.h +9 -0
- data/src/JavascriptInvocation.cpp +1 -1
- data/src/JavascriptInvocation.h +1 -1
- data/src/JavascriptPromptMessages.cpp +10 -0
- data/src/JavascriptPromptMessages.h +9 -0
- data/src/NetworkAccessManager.cpp +38 -5
- data/src/NetworkAccessManager.h +20 -0
- data/src/Node.cpp +6 -1
- data/src/Node.h +4 -5
- data/src/NullCommand.cpp +5 -2
- data/src/NullCommand.h +4 -3
- data/src/PageLoadingCommand.cpp +12 -7
- data/src/PageLoadingCommand.h +6 -9
- data/src/Render.cpp +2 -1
- data/src/Render.h +3 -5
- data/src/RequestedUrl.cpp +2 -1
- data/src/RequestedUrl.h +3 -5
- data/src/Reset.cpp +3 -17
- data/src/Reset.h +3 -8
- data/src/ResizeWindow.cpp +2 -1
- data/src/ResizeWindow.h +3 -5
- data/src/Response.cpp +4 -0
- data/src/Response.h +1 -0
- data/src/Server.cpp +2 -3
- data/src/Server.h +0 -2
- data/src/SetConfirmAction.cpp +11 -0
- data/src/SetConfirmAction.h +9 -0
- data/src/SetCookie.cpp +3 -4
- data/src/SetCookie.h +3 -5
- data/src/SetPromptAction.cpp +11 -0
- data/src/SetPromptAction.h +9 -0
- data/src/SetPromptText.cpp +11 -0
- data/src/SetPromptText.h +9 -0
- data/src/SetProxy.cpp +2 -1
- data/src/SetProxy.h +3 -5
- data/src/SetSkipImageLoading.cpp +12 -0
- data/src/SetSkipImageLoading.h +9 -0
- data/src/SetTimeout.cpp +19 -0
- data/src/SetTimeout.h +9 -0
- data/src/SocketCommand.cpp +21 -0
- data/src/SocketCommand.h +29 -0
- data/src/Source.cpp +3 -2
- data/src/Source.h +3 -4
- data/src/Status.cpp +2 -1
- data/src/Status.h +3 -5
- data/src/TimeoutCommand.cpp +69 -0
- data/src/TimeoutCommand.h +41 -0
- data/src/UnsupportedContentHandler.cpp +11 -17
- data/src/UnsupportedContentHandler.h +5 -3
- data/src/Url.cpp +2 -1
- data/src/Url.h +3 -5
- data/src/Visit.cpp +3 -2
- data/src/Visit.h +3 -5
- data/src/WebPage.cpp +129 -44
- data/src/WebPage.h +37 -11
- data/src/WebPageManager.cpp +127 -0
- data/src/WebPageManager.h +59 -0
- data/src/WindowFocus.cpp +32 -0
- data/src/WindowFocus.h +15 -0
- data/src/body.cpp +2 -1
- data/src/capybara.js +38 -10
- data/src/find_command.h +17 -2
- data/src/main.cpp +0 -2
- data/src/webkit_server.pro +36 -0
- data/templates/Command.cpp +2 -4
- data/templates/Command.h +3 -3
- metadata +106 -27
- data/ChangeLog +0 -70
- data/lib/capybara/driver/webkit.rb +0 -136
data/spec/connection_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'capybara/
|
2
|
+
require 'capybara/webkit/connection'
|
3
3
|
|
4
|
-
describe Capybara::
|
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::
|
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::
|
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::
|
53
|
+
let(:connection) { Capybara::Webkit::Connection.new }
|
43
54
|
|
44
55
|
before(:all) do
|
45
56
|
@app = lambda do |env|
|
data/spec/cookie_jar_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'capybara/
|
2
|
+
require 'capybara/webkit/cookie_jar'
|
3
3
|
|
4
|
-
describe Capybara::
|
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::
|
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
|
2
|
+
require 'capybara/webkit/driver'
|
3
3
|
require 'mini_magick'
|
4
4
|
|
5
|
-
describe Capybara::Driver
|
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(:
|
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
|
-
|
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
|
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
|
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
|
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
|
2
|
+
require 'capybara/webkit/driver'
|
3
3
|
|
4
|
-
describe Capybara::Driver
|
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
|
30
|
+
@driver = Capybara::Webkit::Driver.new(app, :browser => $webkit_browser)
|
31
31
|
end
|
32
32
|
|
33
33
|
DEFAULT_DIMENTIONS = "[1680x1050]"
|
data/spec/driver_spec.rb
CHANGED
@@ -1,117 +1,154 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'capybara/driver
|
2
|
+
require 'capybara/webkit/driver'
|
3
|
+
require 'base64'
|
3
4
|
|
4
|
-
describe Capybara::Driver
|
5
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
47
|
-
|
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
|
-
|
53
|
-
|
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 {
|
59
|
-
to raise_error(Capybara::
|
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 {
|
64
|
-
to raise_error(Capybara::
|
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
|
-
|
69
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
81
|
-
|
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
|
-
|
88
|
-
|
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
|
-
|
94
|
-
result =
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
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
|
-
|
221
|
+
driver.response_headers["Content-Type"].should == "text/css"
|
166
222
|
end
|
167
223
|
end
|
168
224
|
|
169
225
|
context "hello app" do
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
<
|
174
|
-
<
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
<
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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 not normalized </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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
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
|
-
|
211
|
-
|
267
|
+
driver.reset!
|
268
|
+
driver.find("//*[contains(., 'hello')]").should be_empty
|
212
269
|
end
|
213
270
|
|
214
|
-
it "has a location
|
215
|
-
|
216
|
-
|
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 {
|
221
|
-
to raise_error(Capybara::
|
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
|
-
|
282
|
+
driver.find("//p").first["id"].should == "greeting"
|
226
283
|
end
|
227
284
|
|
228
285
|
it "parses xpath with quotes" do
|
229
|
-
|
286
|
+
driver.find('//*[contains(., "hello")]').should_not be_empty
|
230
287
|
end
|
231
288
|
|
232
289
|
it "returns a node's text" do
|
233
|
-
|
290
|
+
driver.find("//p").first.text.should == "hello"
|
234
291
|
end
|
235
292
|
|
236
293
|
it "normalizes a node's text" do
|
237
|
-
|
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
|
-
|
242
|
-
|
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
|
-
|
247
|
-
|
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
|
-
|
253
|
-
|
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
|
-
|
259
|
-
|
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
|
-
|
264
|
-
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
323
|
-
|
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 {
|
328
|
-
to raise_error(Capybara::
|
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 {
|
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
|
-
|
392
|
+
driver.find("//p").first.tag_name.should == "p"
|
338
393
|
end
|
339
394
|
|
340
395
|
it "reads disabled property" do
|
341
|
-
|
396
|
+
driver.find("//input").first.should be_disabled
|
342
397
|
end
|
343
398
|
|
344
399
|
it "reads checked property" do
|
345
|
-
|
400
|
+
driver.find("//input[@id='checktest']").first.should be_checked
|
346
401
|
end
|
347
402
|
|
348
403
|
it "finds visible elements" do
|
349
|
-
|
350
|
-
|
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
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
383
|
-
|
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
|
-
|
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
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
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
|
-
|
419
|
-
|
420
|
-
|
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
|
-
|
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
|
-
|
660
|
+
driver.find("//input").first.value.should == "bar"
|
430
661
|
end
|
431
662
|
|
432
663
|
it "returns a select's value" do
|
433
|
-
|
664
|
+
driver.find("//select").first.value.should == "Capybara"
|
434
665
|
end
|
435
666
|
|
436
667
|
it "sets an input's value" do
|
437
|
-
input =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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) {
|
479
|
-
let(:capybara_option) {
|
480
|
-
let(:animal_select) {
|
481
|
-
let(:apple_option) {
|
482
|
-
let(:banana_option) {
|
483
|
-
let(:cherry_option) {
|
484
|
-
let(:toppings_select) {
|
485
|
-
let(:
|
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) {
|
541
|
-
let(:unchecked_box) {
|
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) {
|
580
|
-
let(:disabled_input) {
|
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
|
-
|
593
|
-
|
594
|
-
body
|
595
|
-
|
596
|
-
<
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
var
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
var
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
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
|
-
|
625
|
-
|
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
|
-
|
631
|
-
|
632
|
-
body
|
633
|
-
<
|
634
|
-
<
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
var
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
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
|
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
|
-
|
689
|
-
|
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
|
-
|
695
|
-
|
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
|
-
|
700
|
-
|
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
|
-
|
705
|
-
|
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
|
-
|
711
|
-
|
712
|
-
body
|
713
|
-
<
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
<
|
718
|
-
<
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
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
|
-
|
752
|
-
|
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
|
-
|
757
|
-
|
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
|
-
|
762
|
-
|
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 =
|
1001
|
+
select = driver.find("//select").first
|
767
1002
|
select.value.should == "1"
|
768
|
-
option =
|
1003
|
+
option = driver.find("//option[@id='option-2']").first
|
769
1004
|
option.select_option
|
770
1005
|
select.value.should == "2"
|
771
|
-
|
1006
|
+
driver.find("//select[@class='triggered']").should_not be_empty
|
772
1007
|
end
|
773
1008
|
|
774
1009
|
it "fires drag events" do
|
775
|
-
draggable =
|
776
|
-
container =
|
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
|
-
|
1015
|
+
driver.find("//*[@class='triggered']").size.should == 1
|
781
1016
|
end
|
782
1017
|
end
|
783
1018
|
|
784
1019
|
context "nesting app" do
|
785
|
-
|
786
|
-
|
787
|
-
body
|
788
|
-
<
|
789
|
-
<div
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
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 =
|
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
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
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
|
-
|
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
|
-
|
823
|
-
|
824
|
-
|
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
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
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
|
-
|
1080
|
+
driver.find("//input").first.click
|
849
1081
|
wait_for_error_to_complete
|
850
|
-
|
1082
|
+
driver.find("//body")
|
851
1083
|
}.
|
852
|
-
to raise_error(Capybara::
|
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
|
-
|
862
|
-
|
863
|
-
|
864
|
-
body = "error"
|
1093
|
+
let(:driver) do
|
1094
|
+
driver_for_app do
|
1095
|
+
get "/error" do
|
865
1096
|
sleep(1)
|
866
|
-
|
867
|
-
|
868
|
-
|
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
|
-
|
883
|
-
expect {
|
884
|
-
|
885
|
-
|
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
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
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
|
-
|
1143
|
+
driver.find("//p").first.text.should == "success"
|
911
1144
|
end
|
912
1145
|
end
|
913
1146
|
|
914
1147
|
context "custom header" do
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
<
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
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
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
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
|
-
|
940
|
-
|
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
|
-
|
945
|
-
|
946
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
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
|
-
|
969
|
-
|
970
|
-
body
|
971
|
-
<
|
972
|
-
|
973
|
-
|
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
|
-
|
1215
|
+
driver.find("//body")
|
985
1216
|
}.
|
986
|
-
to raise_error(Capybara::
|
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
|
-
|
992
|
-
|
993
|
-
|
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
|
-
|
998
|
-
|
999
|
-
|
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
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
<
|
1008
|
-
<
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
<
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
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 =
|
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
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
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
|
-
|
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
|
-
|
1283
|
+
driver.visit "/"
|
1060
1284
|
echoed_cookie.should == "abc"
|
1061
1285
|
end
|
1062
1286
|
|
1063
1287
|
it "uses a custom cookie" do
|
1064
|
-
|
1065
|
-
|
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
|
-
|
1071
|
-
|
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 =
|
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
|
-
|
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
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
<
|
1134
|
-
<
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
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 =
|
1156
|
-
|
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
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
<
|
1166
|
-
<
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
<
|
1172
|
-
<
|
1173
|
-
<
|
1174
|
-
<
|
1175
|
-
|
1176
|
-
|
1177
|
-
<
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
<
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
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 =
|
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
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
<
|
1232
|
-
<
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
<
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
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
|
-
|
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
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
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 = "/
|
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
|
-
|
1268
|
-
|
1269
|
-
|
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
|
-
|
1276
|
-
|
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
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
<
|
1286
|
-
<
|
1287
|
-
|
1288
|
-
|
1289
|
-
if (
|
1290
|
-
|
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
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
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
|
-
|
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
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
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
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
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
|
-
|
1406
|
-
|
1513
|
+
driver.find("//form")[0].submit
|
1514
|
+
driver.body.should include "Congrats"
|
1407
1515
|
end
|
1408
1516
|
end
|
1409
1517
|
|
1410
|
-
def
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
<
|
1415
|
-
<
|
1416
|
-
<
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
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
|
-
|
1439
|
-
|
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
|
-
|
1444
|
-
|
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
|
-
|
1449
|
-
|
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
|
-
|
1454
|
-
|
1455
|
-
|
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
|
-
|
1519
|
-
|
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
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
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
|
-
|
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
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
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
|
-
|
1549
|
-
|
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
|