capybara-webkit 0.7.2 → 0.8.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/CONTRIBUTING.md +1 -1
- data/Gemfile +1 -6
- data/Gemfile.lock +15 -13
- data/README.md +9 -8
- data/Rakefile +3 -21
- data/capybara-webkit.gemspec +21 -18
- data/gemfiles/1.0.gemfile.lock +18 -13
- data/gemfiles/1.1.gemfile.lock +18 -13
- data/lib/capybara/driver/webkit.rb +20 -1
- data/lib/capybara/driver/webkit/browser.rb +49 -5
- data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
- data/lib/capybara/driver/webkit/node.rb +5 -1
- data/lib/capybara/driver/webkit/version.rb +7 -0
- data/lib/capybara/webkit/matchers.rb +39 -0
- data/lib/capybara_webkit_builder.rb +2 -1
- data/spec/browser_spec.rb +150 -2
- data/spec/cookie_jar_spec.rb +48 -0
- data/spec/driver_spec.rb +248 -16
- data/spec/self_signed_ssl_cert.rb +42 -0
- data/src/CommandFactory.cpp +31 -0
- data/src/CommandFactory.h +16 -0
- data/src/CommandParser.cpp +68 -0
- data/src/CommandParser.h +29 -0
- data/src/Connection.cpp +14 -83
- data/src/Connection.h +5 -8
- data/src/ConsoleMessages.cpp +11 -0
- data/src/ConsoleMessages.h +12 -0
- data/src/RequestedUrl.cpp +15 -0
- data/src/RequestedUrl.h +12 -0
- data/src/Reset.cpp +1 -0
- data/src/Server.cpp +2 -1
- data/src/Server.h +1 -1
- data/src/SetProxy.cpp +24 -0
- data/src/SetProxy.h +11 -0
- data/src/UnsupportedContentHandler.cpp +1 -0
- data/src/Visit.cpp +1 -1
- data/src/WebPage.cpp +28 -2
- data/src/WebPage.h +7 -0
- data/src/capybara.js +54 -11
- data/src/find_command.h +4 -0
- data/src/main.cpp +4 -1
- data/src/webkit_server.pro +10 -0
- metadata +67 -47
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
|
3
|
+
# A simple cookie jar implementation.
|
4
|
+
# Does not take special cookie attributes
|
5
|
+
# into account like expire, max-age, httponly, secure
|
6
|
+
class Capybara::Driver::Webkit::CookieJar
|
7
|
+
attr_reader :browser
|
8
|
+
|
9
|
+
def initialize(browser)
|
10
|
+
@browser = browser
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](*args)
|
14
|
+
cookie = find(*args)
|
15
|
+
cookie && cookie.value
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(name, domain = nil, path = "/")
|
19
|
+
# we are sorting by path size because more specific paths take
|
20
|
+
# precendence
|
21
|
+
cookies.sort_by { |c| -c.path.size }.find { |c|
|
22
|
+
c.name.downcase == name.downcase &&
|
23
|
+
(!domain || valid_domain?(c, domain)) &&
|
24
|
+
(!path || valid_path?(c, path))
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def valid_domain?(cookie, domain)
|
31
|
+
ends_with?(("." + domain).downcase,
|
32
|
+
normalize_domain(cookie.domain).downcase)
|
33
|
+
end
|
34
|
+
|
35
|
+
def normalize_domain(domain)
|
36
|
+
domain = "." + domain unless domain[0,1] == "."
|
37
|
+
domain
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_path?(cookie, path)
|
41
|
+
starts_with?(path, cookie.path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ends_with?(str, suffix)
|
45
|
+
str[-suffix.size..-1] == suffix
|
46
|
+
end
|
47
|
+
|
48
|
+
def starts_with?(str, prefix)
|
49
|
+
str[0, prefix.size] == prefix
|
50
|
+
end
|
51
|
+
|
52
|
+
def cookies
|
53
|
+
browser.get_cookies.map { |c| WEBrick::Cookie.parse_set_cookie(c) }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Webkit
|
3
|
+
module RspecMatchers
|
4
|
+
extend RSpec::Matchers::DSL
|
5
|
+
|
6
|
+
matcher :have_errors do |expected|
|
7
|
+
match do |actual|
|
8
|
+
actual = resolve(actual)
|
9
|
+
actual.error_messages.any?
|
10
|
+
end
|
11
|
+
|
12
|
+
failure_message_for_should do |actual|
|
13
|
+
"Expected Javascript errors, but there were none."
|
14
|
+
end
|
15
|
+
failure_message_for_should_not do |actual|
|
16
|
+
actual = resolve(actual)
|
17
|
+
"Expected no Javascript errors, got:\n#{error_messages_for(actual)}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def error_messages_for(obj)
|
21
|
+
obj.error_messages.map do |m|
|
22
|
+
" - #{m[:message]}"
|
23
|
+
end.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve(actual)
|
27
|
+
if actual.respond_to? :page
|
28
|
+
actual.page.driver
|
29
|
+
elsif actual.respond_to? :driver
|
30
|
+
actual.driver
|
31
|
+
else
|
32
|
+
actual
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fileutils"
|
2
|
+
require "rbconfig"
|
2
3
|
|
3
4
|
module CapybaraWebkitBuilder
|
4
5
|
extend self
|
@@ -11,7 +12,7 @@ module CapybaraWebkitBuilder
|
|
11
12
|
def makefile
|
12
13
|
qmake_binaries = ['qmake', 'qmake-qt4']
|
13
14
|
qmake = qmake_binaries.detect { |qmake| system("which #{qmake}") }
|
14
|
-
case
|
15
|
+
case RbConfig::CONFIG['host_os']
|
15
16
|
when /linux/
|
16
17
|
system("#{qmake} -spec linux-g++")
|
17
18
|
when /freebsd/
|
data/spec/browser_spec.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'self_signed_ssl_cert'
|
2
3
|
require 'stringio'
|
3
4
|
require 'capybara/driver/webkit/browser'
|
5
|
+
require 'socket'
|
6
|
+
require 'base64'
|
4
7
|
|
5
8
|
describe Capybara::Driver::Webkit::Browser do
|
6
9
|
|
7
10
|
let(:browser) { Capybara::Driver::Webkit::Browser.new }
|
11
|
+
let(:browser_ignore_ssl_err) {
|
12
|
+
Capybara::Driver::Webkit::Browser.new(:ignore_ssl_errors => true)
|
13
|
+
}
|
8
14
|
|
9
15
|
describe '#server_port' do
|
10
16
|
subject { browser.server_port }
|
@@ -23,13 +29,155 @@ describe Capybara::Driver::Webkit::Browser do
|
|
23
29
|
new_browser.server_port.should_not == browser.server_port
|
24
30
|
end
|
25
31
|
end
|
26
|
-
|
32
|
+
|
27
33
|
it 'forwards stdout to the given IO object' do
|
28
34
|
io = StringIO.new
|
29
35
|
new_browser = Capybara::Driver::Webkit::Browser.new(:stdout => io)
|
30
36
|
new_browser.execute_script('console.log("hello world")')
|
31
37
|
sleep(0.5)
|
32
|
-
io.string.should
|
38
|
+
io.string.should include "hello world\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'handling of SSL validation errors' do
|
42
|
+
before do
|
43
|
+
# set up minimal HTTPS server
|
44
|
+
@host = "127.0.0.1"
|
45
|
+
@server = TCPServer.new(@host, 0)
|
46
|
+
@port = @server.addr[1]
|
47
|
+
|
48
|
+
# set up SSL layer
|
49
|
+
ssl_serv = OpenSSL::SSL::SSLServer.new(@server, $openssl_self_signed_ctx)
|
50
|
+
|
51
|
+
@server_thread = Thread.new(ssl_serv) do |serv|
|
52
|
+
while conn = serv.accept do
|
53
|
+
# read request
|
54
|
+
request = []
|
55
|
+
until (line = conn.readline.strip).empty?
|
56
|
+
request << line
|
57
|
+
end
|
58
|
+
|
59
|
+
# write response
|
60
|
+
html = "<html><body>D'oh!</body></html>"
|
61
|
+
conn.write "HTTP/1.1 200 OK\r\n"
|
62
|
+
conn.write "Content-Type:text/html\r\n"
|
63
|
+
conn.write "Content-Length: %i\r\n" % html.size
|
64
|
+
conn.write "\r\n"
|
65
|
+
conn.write html
|
66
|
+
conn.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
@server_thread.kill
|
73
|
+
@server.close
|
74
|
+
end
|
75
|
+
|
76
|
+
it "doesn't accept a self-signed certificate by default" do
|
77
|
+
lambda { browser.visit "https://#{@host}:#{@port}/" }.should raise_error
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'accepts a self-signed certificate if configured to do so' do
|
81
|
+
browser_ignore_ssl_err.visit "https://#{@host}:#{@port}/"
|
82
|
+
end
|
33
83
|
end
|
84
|
+
describe "forking" do
|
85
|
+
it "only shuts down the server from the main process" do
|
86
|
+
browser.reset!
|
87
|
+
pid = fork {}
|
88
|
+
Process.wait(pid)
|
89
|
+
expect { browser.reset! }.not_to raise_error
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#set_proxy' do
|
94
|
+
before do
|
95
|
+
@host = '127.0.0.1'
|
96
|
+
@user = 'user'
|
97
|
+
@pass = 'secret'
|
98
|
+
@url = "http://example.org/"
|
99
|
+
|
100
|
+
@server = TCPServer.new(@host, 0)
|
101
|
+
@port = @server.addr[1]
|
102
|
+
|
103
|
+
@proxy_requests = []
|
104
|
+
@proxy = Thread.new(@server, @proxy_requests) do |serv, proxy_requests|
|
105
|
+
while conn = serv.accept do
|
106
|
+
# read request
|
107
|
+
request = []
|
108
|
+
until (line = conn.readline.strip).empty?
|
109
|
+
request << line
|
110
|
+
end
|
34
111
|
|
112
|
+
# send response
|
113
|
+
auth_header = request.find { |h| h =~ /Authorization:/i }
|
114
|
+
if auth_header || request[0].split(/\s+/)[1] =~ /^\//
|
115
|
+
html = "<html><body>D'oh!</body></html>"
|
116
|
+
conn.write "HTTP/1.1 200 OK\r\n"
|
117
|
+
conn.write "Content-Type:text/html\r\n"
|
118
|
+
conn.write "Content-Length: %i\r\n" % html.size
|
119
|
+
conn.write "\r\n"
|
120
|
+
conn.write html
|
121
|
+
conn.close
|
122
|
+
proxy_requests << request if auth_header
|
123
|
+
else
|
124
|
+
conn.write "HTTP/1.1 407 Proxy Auth Required\r\n"
|
125
|
+
conn.write "Proxy-Authenticate: Basic realm=\"Proxy\"\r\n"
|
126
|
+
conn.write "\r\n"
|
127
|
+
conn.close
|
128
|
+
proxy_requests << request
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
browser.set_proxy(:host => @host,
|
134
|
+
:port => @port,
|
135
|
+
:user => @user,
|
136
|
+
:pass => @pass)
|
137
|
+
browser.visit @url
|
138
|
+
@proxy_requests.size.should == 2
|
139
|
+
@request = @proxy_requests[-1]
|
140
|
+
end
|
141
|
+
|
142
|
+
after do
|
143
|
+
@proxy.kill
|
144
|
+
@server.close
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'uses the HTTP proxy correctly' do
|
148
|
+
@request[0].should match /^GET\s+http:\/\/example.org\/\s+HTTP/i
|
149
|
+
@request.find { |header|
|
150
|
+
header =~ /^Host:\s+example.org$/i }.should_not be nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'sends correct proxy authentication' do
|
154
|
+
auth_header = @request.find { |header|
|
155
|
+
header =~ /^Proxy-Authorization:\s+/i }
|
156
|
+
auth_header.should_not be nil
|
157
|
+
|
158
|
+
user, pass = Base64.decode64(auth_header.split(/\s+/)[-1]).split(":")
|
159
|
+
user.should == @user
|
160
|
+
pass.should == @pass
|
161
|
+
end
|
162
|
+
|
163
|
+
it "uses the proxies' response" do
|
164
|
+
browser.body.should include "D'oh!"
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'uses original URL' do
|
168
|
+
browser.url.should == @url
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'uses URLs changed by javascript' do
|
172
|
+
browser.execute_script "window.history.pushState('', '', '/blah')"
|
173
|
+
browser.requested_url.should == 'http://example.org/blah'
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'is possible to disable proxy again' do
|
177
|
+
@proxy_requests.clear
|
178
|
+
browser.clear_proxy
|
179
|
+
browser.visit "http://#{@host}:#{@port}/"
|
180
|
+
@proxy_requests.size.should == 0
|
181
|
+
end
|
182
|
+
end
|
35
183
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'capybara/driver/webkit/cookie_jar'
|
3
|
+
|
4
|
+
describe Capybara::Driver::Webkit::CookieJar do
|
5
|
+
let(:browser) {
|
6
|
+
browser = double("Browser")
|
7
|
+
browser.stub(:get_cookies) { [
|
8
|
+
"cookie1=1; domain=.example.org; path=/",
|
9
|
+
"cookie1=2; domain=.example.org; path=/dir1/",
|
10
|
+
"cookie1=3; domain=.facebook.com; path=/",
|
11
|
+
"cookie2=4; domain=.sub1.example.org; path=/",
|
12
|
+
] }
|
13
|
+
browser
|
14
|
+
}
|
15
|
+
|
16
|
+
subject { Capybara::Driver::Webkit::CookieJar.new(browser) }
|
17
|
+
|
18
|
+
describe "#find" do
|
19
|
+
it "returns a cookie object" do
|
20
|
+
subject.find("cookie1", "www.facebook.com").domain.should == ".facebook.com"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the right cookie for every given domain/path" do
|
24
|
+
subject.find("cookie1", "example.org").value.should == "1"
|
25
|
+
subject.find("cookie1", "www.facebook.com").value.should == "3"
|
26
|
+
subject.find("cookie2", "sub1.example.org").value.should == "4"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not return a cookie from other domain" do
|
30
|
+
subject.find("cookie2", "www.example.org").should == nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "respects path precedence rules" do
|
34
|
+
subject.find("cookie1", "www.example.org").value.should == "1"
|
35
|
+
subject.find("cookie1", "www.example.org", "/dir1/123").value.should == "2"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#[]" do
|
40
|
+
it "returns the first matching cookie's value" do
|
41
|
+
subject["cookie1", "example.org"].should == "1"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns nil if no cookie is found" do
|
45
|
+
subject["notexisting"].should == nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/driver_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe Capybara::Driver::Webkit do
|
|
20
20
|
p_id = "greeting"
|
21
21
|
msg = "hello"
|
22
22
|
iframe = "<iframe id=\"f\" src=\"/?iframe=true\"></iframe>"
|
23
|
-
end
|
23
|
+
end
|
24
24
|
body = <<-HTML
|
25
25
|
<html>
|
26
26
|
<head>
|
@@ -140,12 +140,16 @@ describe Capybara::Driver::Webkit do
|
|
140
140
|
@app = lambda do |env|
|
141
141
|
[200, {"Content-Type" => "text/css", "Content-Length" => body.length.to_s}, [body]]
|
142
142
|
end
|
143
|
+
subject.visit("/")
|
143
144
|
end
|
144
145
|
|
145
146
|
it "renders unsupported content types gracefully" do
|
146
|
-
subject.visit("/")
|
147
147
|
subject.body.should =~ /css/
|
148
148
|
end
|
149
|
+
|
150
|
+
it "sets the response headers with respect to the unsupported request" do
|
151
|
+
subject.response_headers["Content-Type"].should == "text/css"
|
152
|
+
end
|
149
153
|
end
|
150
154
|
|
151
155
|
context "hello app" do
|
@@ -219,9 +223,9 @@ describe Capybara::Driver::Webkit do
|
|
219
223
|
subject.current_url.should == "http://127.0.0.1:#{port}/hello/world?success=true"
|
220
224
|
end
|
221
225
|
|
222
|
-
it "
|
223
|
-
subject.visit("/hello
|
224
|
-
subject.current_url.should =~ /
|
226
|
+
it "does not double-encode URLs" do
|
227
|
+
subject.visit("/hello/world?success=%25true")
|
228
|
+
subject.current_url.should =~ /success=\%25true/
|
225
229
|
end
|
226
230
|
|
227
231
|
it "visits a page with an anchor" do
|
@@ -316,6 +320,40 @@ describe Capybara::Driver::Webkit do
|
|
316
320
|
end
|
317
321
|
end
|
318
322
|
|
323
|
+
context "console messages app" do
|
324
|
+
|
325
|
+
before(:all) do
|
326
|
+
@app = lambda do |env|
|
327
|
+
body = <<-HTML
|
328
|
+
<html>
|
329
|
+
<head>
|
330
|
+
</head>
|
331
|
+
<body>
|
332
|
+
<script type="text/javascript">
|
333
|
+
console.log("hello");
|
334
|
+
console.log("hello again");
|
335
|
+
oops
|
336
|
+
</script>
|
337
|
+
</body>
|
338
|
+
</html>
|
339
|
+
HTML
|
340
|
+
[200,
|
341
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
|
342
|
+
[body]]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
it "collects messages logged to the console" do
|
347
|
+
subject.console_messages.first.should include :source, :message => "hello", :line_number => 6
|
348
|
+
subject.console_messages.length.should eq 3
|
349
|
+
end
|
350
|
+
|
351
|
+
it "logs errors to the console" do
|
352
|
+
subject.error_messages.length.should eq 1
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
319
357
|
context "form app" do
|
320
358
|
before(:all) do
|
321
359
|
@app = lambda do |env|
|
@@ -497,8 +535,13 @@ describe Capybara::Driver::Webkit do
|
|
497
535
|
body = <<-HTML
|
498
536
|
<html><body>
|
499
537
|
<form action="/" method="GET">
|
500
|
-
<input class="watch" type="
|
538
|
+
<input class="watch" type="email"/>
|
539
|
+
<input class="watch" type="number"/>
|
501
540
|
<input class="watch" type="password"/>
|
541
|
+
<input class="watch" type="search"/>
|
542
|
+
<input class="watch" type="tel"/>
|
543
|
+
<input class="watch" type="text"/>
|
544
|
+
<input class="watch" type="url"/>
|
502
545
|
<textarea class="watch"></textarea>
|
503
546
|
<input class="watch" type="checkbox"/>
|
504
547
|
<input class="watch" type="radio"/>
|
@@ -540,9 +583,11 @@ describe Capybara::Driver::Webkit do
|
|
540
583
|
%w{change blur}).flatten
|
541
584
|
end
|
542
585
|
|
543
|
-
|
544
|
-
|
545
|
-
|
586
|
+
%w(email number password search tel text url).each do | field_type |
|
587
|
+
it "triggers text input events on inputs of type #{field_type}" do
|
588
|
+
subject.find("//input[@type='#{field_type}']").first.set(newtext)
|
589
|
+
subject.find("//li").map(&:text).should == keyevents
|
590
|
+
end
|
546
591
|
end
|
547
592
|
|
548
593
|
it "triggers textarea input events" do
|
@@ -550,11 +595,6 @@ describe Capybara::Driver::Webkit do
|
|
550
595
|
subject.find("//li").map(&:text).should == keyevents
|
551
596
|
end
|
552
597
|
|
553
|
-
it "triggers password input events" do
|
554
|
-
subject.find("//input[@type='password']").first.set(newtext)
|
555
|
-
subject.find("//li").map(&:text).should == keyevents
|
556
|
-
end
|
557
|
-
|
558
598
|
it "triggers radio input events" do
|
559
599
|
subject.find("//input[@type='radio']").first.set(true)
|
560
600
|
subject.find("//li").map(&:text).should == %w(click change)
|
@@ -838,7 +878,7 @@ describe Capybara::Driver::Webkit do
|
|
838
878
|
[body]]
|
839
879
|
end
|
840
880
|
end
|
841
|
-
|
881
|
+
|
842
882
|
it "raises a webkit error for the requested url" do
|
843
883
|
make_the_server_go_away
|
844
884
|
expect {
|
@@ -853,7 +893,7 @@ describe Capybara::Driver::Webkit do
|
|
853
893
|
subject.browser.instance_variable_get(:@socket).unstub!(:puts)
|
854
894
|
subject.browser.instance_variable_get(:@socket).unstub!(:print)
|
855
895
|
end
|
856
|
-
|
896
|
+
|
857
897
|
def make_the_server_go_away
|
858
898
|
subject.browser.instance_variable_get(:@socket).stub!(:gets).and_return(nil)
|
859
899
|
subject.browser.instance_variable_get(:@socket).stub!(:puts)
|
@@ -943,6 +983,10 @@ describe Capybara::Driver::Webkit do
|
|
943
983
|
cookie["domain"].should include "127.0.0.1"
|
944
984
|
cookie["path"].should == "/"
|
945
985
|
end
|
986
|
+
|
987
|
+
it "allows reading access to cookies using a nice syntax" do
|
988
|
+
subject.cookies["cookie"].should == "abc"
|
989
|
+
end
|
946
990
|
end
|
947
991
|
|
948
992
|
context "with socket debugger" do
|
@@ -1014,6 +1058,98 @@ describe Capybara::Driver::Webkit do
|
|
1014
1058
|
end
|
1015
1059
|
end
|
1016
1060
|
|
1061
|
+
context "app with a lot of HTML tags" do
|
1062
|
+
before(:all) do
|
1063
|
+
@app = lambda do |env|
|
1064
|
+
body = <<-HTML
|
1065
|
+
<html>
|
1066
|
+
<head>
|
1067
|
+
<title>My eBook</title>
|
1068
|
+
<meta class="charset" name="charset" value="utf-8" />
|
1069
|
+
<meta class="author" name="author" value="Firstname Lastname" />
|
1070
|
+
</head>
|
1071
|
+
<body>
|
1072
|
+
<div id="toc">
|
1073
|
+
<table>
|
1074
|
+
<thead id="head">
|
1075
|
+
<tr><td class="td1">Chapter</td><td>Page</td></tr>
|
1076
|
+
</thead>
|
1077
|
+
<tbody>
|
1078
|
+
<tr><td>Intro</td><td>1</td></tr>
|
1079
|
+
<tr><td>Chapter 1</td><td class="td2">1</td></tr>
|
1080
|
+
<tr><td>Chapter 2</td><td>1</td></tr>
|
1081
|
+
</tbody>
|
1082
|
+
</table>
|
1083
|
+
</div>
|
1084
|
+
|
1085
|
+
<h1 class="h1">My first book</h1>
|
1086
|
+
<p class="p1">Written by me</p>
|
1087
|
+
<div id="intro" class="intro">
|
1088
|
+
<p>Let's try out XPath</p>
|
1089
|
+
<p class="p2">in capybara-webkit</p>
|
1090
|
+
</div>
|
1091
|
+
|
1092
|
+
<h2 class="chapter1">Chapter 1</h2>
|
1093
|
+
<p>This paragraph is fascinating.</p>
|
1094
|
+
<p class="p3">But not as much as this one.</p>
|
1095
|
+
|
1096
|
+
<h2 class="chapter2">Chapter 2</h2>
|
1097
|
+
<p>Let's try if we can select this</p>
|
1098
|
+
</body>
|
1099
|
+
</html>
|
1100
|
+
HTML
|
1101
|
+
[200,
|
1102
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
|
1103
|
+
[body]]
|
1104
|
+
end
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
it "builds up node paths correctly" do
|
1108
|
+
cases = {
|
1109
|
+
"//*[contains(@class, 'author')]" => "/html/head/meta[2]",
|
1110
|
+
"//*[contains(@class, 'td1')]" => "/html/body/div[@id='toc']/table/thead[@id='head']/tr/td[1]",
|
1111
|
+
"//*[contains(@class, 'td2')]" => "/html/body/div[@id='toc']/table/tbody/tr[2]/td[2]",
|
1112
|
+
"//h1" => "/html/body/h1",
|
1113
|
+
"//*[contains(@class, 'chapter2')]" => "/html/body/h2[2]",
|
1114
|
+
"//*[contains(@class, 'p1')]" => "/html/body/p[1]",
|
1115
|
+
"//*[contains(@class, 'p2')]" => "/html/body/div[@id='intro']/p[2]",
|
1116
|
+
"//*[contains(@class, 'p3')]" => "/html/body/p[3]",
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
cases.each do |xpath, path|
|
1120
|
+
nodes = subject.find(xpath)
|
1121
|
+
nodes.size.should == 1
|
1122
|
+
nodes[0].path.should == path
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
context "css overflow app" do
|
1128
|
+
before(:all) do
|
1129
|
+
@app = lambda do |env|
|
1130
|
+
body = <<-HTML
|
1131
|
+
<html>
|
1132
|
+
<head>
|
1133
|
+
<style type="text/css">
|
1134
|
+
#overflow { overflow: hidden }
|
1135
|
+
</style>
|
1136
|
+
</head>
|
1137
|
+
<body>
|
1138
|
+
<div id="overflow">Overflow</div>
|
1139
|
+
</body>
|
1140
|
+
</html>
|
1141
|
+
HTML
|
1142
|
+
[200,
|
1143
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
|
1144
|
+
[body]]
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
it "handles overflow hidden" do
|
1149
|
+
subject.find("//div[@id='overflow']").first.text.should == "Overflow"
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
|
1017
1153
|
context "javascript redirect app" do
|
1018
1154
|
before(:all) do
|
1019
1155
|
@app = lambda do |env|
|
@@ -1041,4 +1177,100 @@ describe Capybara::Driver::Webkit do
|
|
1041
1177
|
end
|
1042
1178
|
end
|
1043
1179
|
end
|
1180
|
+
|
1181
|
+
context "app with a lot of HTML tags" do
|
1182
|
+
before(:all) do
|
1183
|
+
@app = lambda do |env|
|
1184
|
+
body = <<-HTML
|
1185
|
+
<html>
|
1186
|
+
<head>
|
1187
|
+
<title>My eBook</title>
|
1188
|
+
<meta class="charset" name="charset" value="utf-8" />
|
1189
|
+
<meta class="author" name="author" value="Firstname Lastname" />
|
1190
|
+
</head>
|
1191
|
+
<body>
|
1192
|
+
<div id="toc">
|
1193
|
+
<table>
|
1194
|
+
<thead id="head">
|
1195
|
+
<tr><td class="td1">Chapter</td><td>Page</td></tr>
|
1196
|
+
</thead>
|
1197
|
+
<tbody>
|
1198
|
+
<tr><td>Intro</td><td>1</td></tr>
|
1199
|
+
<tr><td>Chapter 1</td><td class="td2">1</td></tr>
|
1200
|
+
<tr><td>Chapter 2</td><td>1</td></tr>
|
1201
|
+
</tbody>
|
1202
|
+
</table>
|
1203
|
+
</div>
|
1204
|
+
|
1205
|
+
<h1 class="h1">My first book</h1>
|
1206
|
+
<p class="p1">Written by me</p>
|
1207
|
+
<div id="intro" class="intro">
|
1208
|
+
<p>Let's try out XPath</p>
|
1209
|
+
<p class="p2">in capybara-webkit</p>
|
1210
|
+
</div>
|
1211
|
+
|
1212
|
+
<h2 class="chapter1">Chapter 1</h2>
|
1213
|
+
<p>This paragraph is fascinating.</p>
|
1214
|
+
<p class="p3">But not as much as this one.</p>
|
1215
|
+
|
1216
|
+
<h2 class="chapter2">Chapter 2</h2>
|
1217
|
+
<p>Let's try if we can select this</p>
|
1218
|
+
</body>
|
1219
|
+
</html>
|
1220
|
+
HTML
|
1221
|
+
[200,
|
1222
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
|
1223
|
+
[body]]
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
it "builds up node paths correctly" do
|
1228
|
+
cases = {
|
1229
|
+
"//*[contains(@class, 'author')]" => "/html/head/meta[2]",
|
1230
|
+
"//*[contains(@class, 'td1')]" => "/html/body/div[@id='toc']/table/thead[@id='head']/tr/td[1]",
|
1231
|
+
"//*[contains(@class, 'td2')]" => "/html/body/div[@id='toc']/table/tbody/tr[2]/td[2]",
|
1232
|
+
"//h1" => "/html/body/h1",
|
1233
|
+
"//*[contains(@class, 'chapter2')]" => "/html/body/h2[2]",
|
1234
|
+
"//*[contains(@class, 'p1')]" => "/html/body/p[1]",
|
1235
|
+
"//*[contains(@class, 'p2')]" => "/html/body/div[@id='intro']/p[2]",
|
1236
|
+
"//*[contains(@class, 'p3')]" => "/html/body/p[3]",
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
cases.each do |xpath, path|
|
1240
|
+
nodes = subject.find(xpath)
|
1241
|
+
nodes.size.should == 1
|
1242
|
+
nodes[0].path.should == path
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
context "form app with server-side handler" do
|
1248
|
+
before(:all) do
|
1249
|
+
@app = lambda do |env|
|
1250
|
+
if env["REQUEST_METHOD"] == "POST"
|
1251
|
+
body = "<html><body><p>Congrats!</p></body></html>"
|
1252
|
+
else
|
1253
|
+
body = <<-HTML
|
1254
|
+
<html>
|
1255
|
+
<head><title>Form</title>
|
1256
|
+
<body>
|
1257
|
+
<form action="/" method="POST">
|
1258
|
+
<input type="hidden" name="abc" value="123" />
|
1259
|
+
<input type="submit" value="Submit" />
|
1260
|
+
</form>
|
1261
|
+
</body>
|
1262
|
+
</html>
|
1263
|
+
HTML
|
1264
|
+
end
|
1265
|
+
[200,
|
1266
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
|
1267
|
+
[body]]
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
it "submits a form without clicking" do
|
1272
|
+
subject.find("//form")[0].submit
|
1273
|
+
subject.body.should include "Congrats"
|
1274
|
+
end
|
1275
|
+
end
|
1044
1276
|
end
|