imseng-capybara-webkit 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +47 -0
  5. data/ChangeLog +70 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +19 -0
  8. data/NEWS.md +36 -0
  9. data/README.md +114 -0
  10. data/Rakefile +65 -0
  11. data/bin/Info.plist +22 -0
  12. data/capybara-webkit.gemspec +28 -0
  13. data/extconf.rb +2 -0
  14. data/gemfiles/1.0.gemfile +7 -0
  15. data/gemfiles/1.0.gemfile.lock +70 -0
  16. data/gemfiles/1.1.gemfile +7 -0
  17. data/gemfiles/1.1.gemfile.lock +70 -0
  18. data/lib/capybara/driver/webkit/browser.rb +164 -0
  19. data/lib/capybara/driver/webkit/connection.rb +120 -0
  20. data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
  21. data/lib/capybara/driver/webkit/node.rb +118 -0
  22. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  23. data/lib/capybara/driver/webkit/version.rb +7 -0
  24. data/lib/capybara/driver/webkit.rb +136 -0
  25. data/lib/capybara/webkit/matchers.rb +37 -0
  26. data/lib/capybara/webkit.rb +13 -0
  27. data/lib/capybara-webkit.rb +1 -0
  28. data/lib/capybara_webkit_builder.rb +68 -0
  29. data/spec/browser_spec.rb +173 -0
  30. data/spec/capybara_webkit_builder_spec.rb +37 -0
  31. data/spec/connection_spec.rb +54 -0
  32. data/spec/cookie_jar_spec.rb +48 -0
  33. data/spec/driver_rendering_spec.rb +80 -0
  34. data/spec/driver_resize_window_spec.rb +59 -0
  35. data/spec/driver_spec.rb +1552 -0
  36. data/spec/integration/driver_spec.rb +20 -0
  37. data/spec/integration/session_spec.rb +137 -0
  38. data/spec/self_signed_ssl_cert.rb +42 -0
  39. data/spec/spec_helper.rb +46 -0
  40. data/src/Body.h +12 -0
  41. data/src/ClearCookies.cpp +15 -0
  42. data/src/ClearCookies.h +11 -0
  43. data/src/Command.cpp +19 -0
  44. data/src/Command.h +31 -0
  45. data/src/CommandFactory.cpp +37 -0
  46. data/src/CommandFactory.h +16 -0
  47. data/src/CommandParser.cpp +76 -0
  48. data/src/CommandParser.h +33 -0
  49. data/src/Connection.cpp +71 -0
  50. data/src/Connection.h +37 -0
  51. data/src/ConsoleMessages.cpp +10 -0
  52. data/src/ConsoleMessages.h +12 -0
  53. data/src/CurrentUrl.cpp +68 -0
  54. data/src/CurrentUrl.h +16 -0
  55. data/src/Evaluate.cpp +84 -0
  56. data/src/Evaluate.h +22 -0
  57. data/src/Execute.cpp +16 -0
  58. data/src/Execute.h +12 -0
  59. data/src/Find.cpp +19 -0
  60. data/src/Find.h +13 -0
  61. data/src/FrameFocus.cpp +66 -0
  62. data/src/FrameFocus.h +28 -0
  63. data/src/GetCookies.cpp +20 -0
  64. data/src/GetCookies.h +14 -0
  65. data/src/Header.cpp +18 -0
  66. data/src/Header.h +11 -0
  67. data/src/Headers.cpp +10 -0
  68. data/src/Headers.h +12 -0
  69. data/src/IgnoreSslErrors.cpp +12 -0
  70. data/src/IgnoreSslErrors.h +12 -0
  71. data/src/JavascriptInvocation.cpp +14 -0
  72. data/src/JavascriptInvocation.h +19 -0
  73. data/src/NetworkAccessManager.cpp +29 -0
  74. data/src/NetworkAccessManager.h +19 -0
  75. data/src/NetworkCookieJar.cpp +101 -0
  76. data/src/NetworkCookieJar.h +15 -0
  77. data/src/Node.cpp +14 -0
  78. data/src/Node.h +13 -0
  79. data/src/NullCommand.cpp +10 -0
  80. data/src/NullCommand.h +11 -0
  81. data/src/PageLoadingCommand.cpp +46 -0
  82. data/src/PageLoadingCommand.h +40 -0
  83. data/src/Render.cpp +18 -0
  84. data/src/Render.h +12 -0
  85. data/src/RequestedUrl.cpp +12 -0
  86. data/src/RequestedUrl.h +12 -0
  87. data/src/Reset.cpp +29 -0
  88. data/src/Reset.h +15 -0
  89. data/src/ResizeWindow.cpp +16 -0
  90. data/src/ResizeWindow.h +12 -0
  91. data/src/Response.cpp +24 -0
  92. data/src/Response.h +15 -0
  93. data/src/Server.cpp +24 -0
  94. data/src/Server.h +21 -0
  95. data/src/SetCookie.cpp +16 -0
  96. data/src/SetCookie.h +11 -0
  97. data/src/SetProxy.cpp +22 -0
  98. data/src/SetProxy.h +11 -0
  99. data/src/Source.cpp +18 -0
  100. data/src/Source.h +19 -0
  101. data/src/Status.cpp +12 -0
  102. data/src/Status.h +12 -0
  103. data/src/UnsupportedContentHandler.cpp +32 -0
  104. data/src/UnsupportedContentHandler.h +18 -0
  105. data/src/Url.cpp +12 -0
  106. data/src/Url.h +12 -0
  107. data/src/Visit.cpp +12 -0
  108. data/src/Visit.h +12 -0
  109. data/src/WebPage.cpp +239 -0
  110. data/src/WebPage.h +58 -0
  111. data/src/body.cpp +10 -0
  112. data/src/capybara.js +315 -0
  113. data/src/find_command.h +29 -0
  114. data/src/main.cpp +33 -0
  115. data/src/webkit_server.pro +85 -0
  116. data/src/webkit_server.qrc +5 -0
  117. data/templates/Command.cpp +10 -0
  118. data/templates/Command.h +12 -0
  119. data/webkit_server.pro +4 -0
  120. metadata +298 -0
@@ -0,0 +1,68 @@
1
+ require "fileutils"
2
+ require "rbconfig"
3
+
4
+ module CapybaraWebkitBuilder
5
+ extend self
6
+
7
+ def make_bin
8
+ ENV['MAKE'] || 'make'
9
+ end
10
+
11
+ def qmake_bin
12
+ ENV['QMAKE'] || 'qmake'
13
+ end
14
+
15
+ def spec
16
+ ENV['SPEC'] || os_spec
17
+ end
18
+
19
+ def os_spec
20
+ case RbConfig::CONFIG['host_os']
21
+ when /linux/
22
+ "linux-g++"
23
+ when /freebsd/
24
+ "freebsd-g++"
25
+ when /mingw32/
26
+ "win32-g++"
27
+ else
28
+ "macx-g++"
29
+ end
30
+ end
31
+
32
+ def makefile
33
+ system("#{qmake_bin} -spec #{spec}")
34
+ end
35
+
36
+ def qmake
37
+ system("#{make_bin} qmake")
38
+ end
39
+
40
+ def path_to_binary
41
+ case RUBY_PLATFORM
42
+ when /mingw32/
43
+ "src/debug/webkit_server.exe"
44
+ else
45
+ "src/webkit_server"
46
+ end
47
+ end
48
+
49
+ def build
50
+ system(make_bin) or return false
51
+
52
+ FileUtils.mkdir("bin") unless File.directory?("bin")
53
+ FileUtils.cp(path_to_binary, "bin", :preserve => true)
54
+ end
55
+
56
+ def clean
57
+ File.open("Makefile", "w") do |file|
58
+ file.print "all:\n\t@echo ok\ninstall:\n\t@echo ok"
59
+ end
60
+ end
61
+
62
+ def build_all
63
+ makefile &&
64
+ qmake &&
65
+ build &&
66
+ clean
67
+ end
68
+ end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+ require 'self_signed_ssl_cert'
3
+ require 'stringio'
4
+ require 'capybara/driver/webkit/browser'
5
+ require 'capybara/driver/webkit/connection'
6
+ require 'socket'
7
+ require 'base64'
8
+
9
+ describe Capybara::Driver::Webkit::Browser do
10
+
11
+ let(:browser) { Capybara::Driver::Webkit::Browser.new(Capybara::Driver::Webkit::Connection.new) }
12
+ let(:browser_ignore_ssl_err) do
13
+ Capybara::Driver::Webkit::Browser.new(Capybara::Driver::Webkit::Connection.new).tap do |browser|
14
+ browser.ignore_ssl_errors
15
+ end
16
+ end
17
+
18
+ context 'handling of SSL validation errors' do
19
+ before do
20
+ # set up minimal HTTPS server
21
+ @host = "127.0.0.1"
22
+ @server = TCPServer.new(@host, 0)
23
+ @port = @server.addr[1]
24
+
25
+ # set up SSL layer
26
+ ssl_serv = OpenSSL::SSL::SSLServer.new(@server, $openssl_self_signed_ctx)
27
+
28
+ @server_thread = Thread.new(ssl_serv) do |serv|
29
+ while conn = serv.accept do
30
+ # read request
31
+ request = []
32
+ until (line = conn.readline.strip).empty?
33
+ request << line
34
+ end
35
+
36
+ # write response
37
+ html = "<html><body>D'oh!</body></html>"
38
+ conn.write "HTTP/1.1 200 OK\r\n"
39
+ conn.write "Content-Type:text/html\r\n"
40
+ conn.write "Content-Length: %i\r\n" % html.size
41
+ conn.write "\r\n"
42
+ conn.write html
43
+ conn.close
44
+ end
45
+ end
46
+ end
47
+
48
+ after do
49
+ @server_thread.kill
50
+ @server.close
51
+ end
52
+
53
+ it "doesn't accept a self-signed certificate by default" do
54
+ lambda { browser.visit "https://#{@host}:#{@port}/" }.should raise_error
55
+ end
56
+
57
+ it 'accepts a self-signed certificate if configured to do so' do
58
+ browser_ignore_ssl_err.visit "https://#{@host}:#{@port}/"
59
+ end
60
+ end
61
+
62
+ describe "forking", :skip_on_windows => true do
63
+ it "only shuts down the server from the main process" do
64
+ browser.reset!
65
+ pid = fork {}
66
+ Process.wait(pid)
67
+ expect { browser.reset! }.not_to raise_error
68
+ end
69
+ end
70
+
71
+ describe '#set_proxy' do
72
+ before do
73
+ @host = '127.0.0.1'
74
+ @user = 'user'
75
+ @pass = 'secret'
76
+ @url = "http://example.org/"
77
+
78
+ @server = TCPServer.new(@host, 0)
79
+ @port = @server.addr[1]
80
+
81
+ @proxy_requests = []
82
+ @proxy = Thread.new(@server, @proxy_requests) do |serv, proxy_requests|
83
+ while conn = serv.accept do
84
+ # read request
85
+ request = []
86
+ until (line = conn.readline.strip).empty?
87
+ request << line
88
+ end
89
+
90
+ # send response
91
+ auth_header = request.find { |h| h =~ /Authorization:/i }
92
+ if auth_header || request[0].split(/\s+/)[1] =~ /^\//
93
+ html = "<html><body>D'oh!</body></html>"
94
+ conn.write "HTTP/1.1 200 OK\r\n"
95
+ conn.write "Content-Type:text/html\r\n"
96
+ conn.write "Content-Length: %i\r\n" % html.size
97
+ conn.write "\r\n"
98
+ conn.write html
99
+ conn.close
100
+ proxy_requests << request if auth_header
101
+ else
102
+ conn.write "HTTP/1.1 407 Proxy Auth Required\r\n"
103
+ conn.write "Proxy-Authenticate: Basic realm=\"Proxy\"\r\n"
104
+ conn.write "\r\n"
105
+ conn.close
106
+ proxy_requests << request
107
+ end
108
+ end
109
+ end
110
+
111
+ browser.set_proxy(:host => @host,
112
+ :port => @port,
113
+ :user => @user,
114
+ :pass => @pass)
115
+ browser.visit @url
116
+ @proxy_requests.size.should == 2
117
+ @request = @proxy_requests[-1]
118
+ end
119
+
120
+ after do
121
+ @proxy.kill
122
+ @server.close
123
+ end
124
+
125
+ it 'uses the HTTP proxy correctly' do
126
+ @request[0].should match /^GET\s+http:\/\/example.org\/\s+HTTP/i
127
+ @request.find { |header|
128
+ header =~ /^Host:\s+example.org$/i }.should_not be nil
129
+ end
130
+
131
+ it 'sends correct proxy authentication' do
132
+ auth_header = @request.find { |header|
133
+ header =~ /^Proxy-Authorization:\s+/i }
134
+ auth_header.should_not be nil
135
+
136
+ user, pass = Base64.decode64(auth_header.split(/\s+/)[-1]).split(":")
137
+ user.should == @user
138
+ pass.should == @pass
139
+ end
140
+
141
+ it "uses the proxies' response" do
142
+ browser.body.should include "D'oh!"
143
+ end
144
+
145
+ it 'uses original URL' do
146
+ browser.url.should == @url
147
+ end
148
+
149
+ it 'uses URLs changed by javascript' do
150
+ browser.execute_script "window.history.pushState('', '', '/blah')"
151
+ browser.requested_url.should == 'http://example.org/blah'
152
+ end
153
+
154
+ it 'is possible to disable proxy again' do
155
+ @proxy_requests.clear
156
+ browser.clear_proxy
157
+ browser.visit "http://#{@host}:#{@port}/"
158
+ @proxy_requests.size.should == 0
159
+ end
160
+ end
161
+
162
+ it "doesn't try to read an empty response" do
163
+ connection = stub("connection")
164
+ connection.stub(:puts)
165
+ connection.stub(:print)
166
+ connection.stub(:gets).and_return("ok\n", "0\n")
167
+ connection.stub(:read).and_raise(StandardError.new("tried to read empty response"))
168
+
169
+ browser = Capybara::Driver::Webkit::Browser.new(connection)
170
+
171
+ expect { browser.visit("/") }.not_to raise_error(/empty response/)
172
+ end
173
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'capybara_webkit_builder'
3
+
4
+ describe CapybaraWebkitBuilder do
5
+ let(:builder) { CapybaraWebkitBuilder }
6
+
7
+ it "will use the env variable for #make_bin" do
8
+ with_env_vars("MAKE" => "fake_make") do
9
+ builder.make_bin.should == "fake_make"
10
+ end
11
+ end
12
+
13
+ it "will use the env variable for #qmake_bin" do
14
+ with_env_vars("QMAKE" => "fake_qmake") do
15
+ builder.qmake_bin.should == "fake_qmake"
16
+ end
17
+ end
18
+
19
+ it "will use the env variable for #os_spec" do
20
+ with_env_vars("SPEC" => "fake_os_spec") do
21
+ builder.spec.should == "fake_os_spec"
22
+ end
23
+ end
24
+
25
+ it "defaults the #make_bin" do
26
+ builder.make_bin.should == 'make'
27
+ end
28
+
29
+ it "defaults the #qmake_bin" do
30
+ builder.qmake_bin.should == 'qmake'
31
+ end
32
+
33
+ it "defaults #spec to the #os_specs" do
34
+ builder.spec.should == builder.os_spec
35
+ end
36
+ end
37
+
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'capybara/driver/webkit/connection'
3
+
4
+ describe Capybara::Driver::Webkit::Connection do
5
+ it "boots a server to talk to" do
6
+ url = @rack_server.url("/")
7
+ connection.puts "Visit"
8
+ connection.puts 1
9
+ connection.puts url.to_s.bytesize
10
+ connection.print url
11
+ connection.gets.should == "ok\n"
12
+ connection.gets.should == "0\n"
13
+ connection.puts "Body"
14
+ connection.puts 0
15
+ connection.gets.should == "ok\n"
16
+ response_length = connection.gets.to_i
17
+ response = connection.read(response_length)
18
+ response.should include("Hey there")
19
+ end
20
+
21
+ it 'forwards stdout to the given IO object' do
22
+ io = StringIO.new
23
+ redirected_connection = Capybara::Driver::Webkit::Connection.new(:stdout => io)
24
+ script = 'console.log("hello world")'
25
+ redirected_connection.puts "Execute"
26
+ redirected_connection.puts 1
27
+ redirected_connection.puts script.to_s.bytesize
28
+ redirected_connection.print script
29
+ sleep(0.5)
30
+ io.string.should include "hello world\n"
31
+ end
32
+
33
+ it "returns the server port" do
34
+ connection.port.should be_between 0x400, 0xffff
35
+ end
36
+
37
+ it "chooses a new port number for a new connection" do
38
+ new_connection = Capybara::Driver::Webkit::Connection.new
39
+ new_connection.port.should_not == connection.port
40
+ end
41
+
42
+ let(:connection) { Capybara::Driver::Webkit::Connection.new }
43
+
44
+ before(:all) do
45
+ @app = lambda do |env|
46
+ body = "<html><body>Hey there</body></html>"
47
+ [200,
48
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s },
49
+ [body]]
50
+ end
51
+ @rack_server = Capybara::Server.new(@app)
52
+ @rack_server.boot
53
+ end
54
+ 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
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'capybara/driver/webkit'
3
+ require 'mini_magick'
4
+
5
+ describe Capybara::Driver::Webkit, "rendering an image" do
6
+
7
+ before(:all) do
8
+ # Set up the tmp directory and file name
9
+ tmp_dir = File.join(PROJECT_ROOT, 'tmp')
10
+ FileUtils.mkdir_p tmp_dir
11
+ @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")
28
+ end
29
+
30
+ after(:all) { @driver.reset! }
31
+
32
+ def render(options)
33
+ FileUtils.rm_f @file_name
34
+ @driver.render @file_name, options
35
+
36
+ @image = MiniMagick::Image.open @file_name
37
+ end
38
+
39
+ context "with default options" do
40
+ before(:all){ render({}) }
41
+
42
+ it "should be a PNG" do
43
+ @image[:format].should == "PNG"
44
+ end
45
+
46
+ it "width default to 1000px (with 15px less for the scrollbar)" do
47
+ @image[:width].should be < 1001
48
+ @image[:width].should be > 1000-17
49
+ end
50
+
51
+ it "height should be at least 10px" do
52
+ @image[:height].should >= 10
53
+ end
54
+ end
55
+
56
+ context "with dimensions set larger than necessary" do
57
+ before(:all){ render(:width => 500, :height => 400) }
58
+
59
+ it "width should match the width given" do
60
+ @image[:width].should == 500
61
+ end
62
+
63
+ it "height should match the height given" do
64
+ @image[:height].should > 10
65
+ end
66
+ end
67
+
68
+ context "with dimensions set smaller than the document's default" do
69
+ before(:all){ render(:width => 50, :height => 10) }
70
+
71
+ it "width should be greater than the width given" do
72
+ @image[:width].should > 50
73
+ end
74
+
75
+ it "height should be greater than the height given" do
76
+ @image[:height].should > 10
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'capybara/driver/webkit'
3
+
4
+ describe Capybara::Driver::Webkit, "#resize_window(width, height)" do
5
+
6
+ before(:all) do
7
+ app = lambda do |env|
8
+ body = <<-HTML
9
+ <html>
10
+ <body>
11
+ <h1 id="dimentions">UNKNOWN</h1>
12
+
13
+ <script>
14
+ window.onload = window.onresize = function(){
15
+ document.getElementById("dimentions").innerHTML = "[" + window.innerWidth + "x" + window.innerHeight + "]";
16
+ };
17
+ </script>
18
+
19
+ </body>
20
+ </html>
21
+ HTML
22
+
23
+ [
24
+ 200,
25
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
26
+ [body]
27
+ ]
28
+ end
29
+
30
+ @driver = Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
31
+ end
32
+
33
+ DEFAULT_DIMENTIONS = "[1680x1050]"
34
+
35
+ it "resizes the window to the specified size" do
36
+ @driver.visit("/")
37
+
38
+ @driver.resize_window(800, 600)
39
+ @driver.body.should include("[800x600]")
40
+
41
+ @driver.resize_window(300, 100)
42
+ @driver.body.should include("[300x100]")
43
+ end
44
+
45
+ it "resizes the window to the specified size even before the document has loaded" do
46
+ @driver.resize_window(800, 600)
47
+ @driver.visit("/")
48
+ @driver.body.should include("[800x600]")
49
+ end
50
+
51
+ it "resets the window to the default size when the driver is reset" do
52
+ @driver.resize_window(800, 600)
53
+ @driver.reset!
54
+ @driver.visit("/")
55
+ @driver.body.should include(DEFAULT_DIMENTIONS)
56
+ end
57
+
58
+ after(:all) { @driver.reset! }
59
+ end