capybara-webkit 0.1.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 +13 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +55 -0
- data/LICENSE +19 -0
- data/README.md +37 -0
- data/Rakefile +76 -0
- data/capybara-webkit.gemspec +15 -0
- data/extconf.rb +2 -0
- data/lib/capybara-webkit.rb +7 -0
- data/lib/capybara/driver/webkit.rb +84 -0
- data/lib/capybara/driver/webkit/browser.rb +86 -0
- data/lib/capybara/driver/webkit/node.rb +87 -0
- data/spec/driver_spec.rb +465 -0
- data/spec/integration/driver_spec.rb +21 -0
- data/spec/integration/session_spec.rb +12 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/socket_debugger.rb +42 -0
- data/src/Command.cpp +15 -0
- data/src/Command.h +28 -0
- data/src/Connection.cpp +130 -0
- data/src/Connection.h +36 -0
- data/src/Evaluate.cpp +84 -0
- data/src/Evaluate.h +22 -0
- data/src/Execute.cpp +17 -0
- data/src/Execute.h +12 -0
- data/src/Find.cpp +20 -0
- data/src/Find.h +13 -0
- data/src/JavascriptInvocation.cpp +14 -0
- data/src/JavascriptInvocation.h +19 -0
- data/src/Node.cpp +14 -0
- data/src/Node.h +13 -0
- data/src/Reset.cpp +16 -0
- data/src/Reset.h +12 -0
- data/src/Server.cpp +21 -0
- data/src/Server.h +20 -0
- data/src/Source.cpp +14 -0
- data/src/Source.h +12 -0
- data/src/Url.cpp +14 -0
- data/src/Url.h +12 -0
- data/src/Visit.cpp +20 -0
- data/src/Visit.h +16 -0
- data/src/WebPage.cpp +81 -0
- data/src/WebPage.h +29 -0
- data/src/capybara.js +98 -0
- data/src/find_command.h +13 -0
- data/src/main.cpp +20 -0
- data/src/webkit_server.pro +9 -0
- data/src/webkit_server.qrc +5 -0
- data/templates/Command.cpp +10 -0
- data/templates/Command.h +12 -0
- data/webkit_server.pro +4 -0
- metadata +140 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
capybara (0.4.1.2)
|
5
|
+
celerity (>= 0.7.9)
|
6
|
+
culerity (>= 0.2.4)
|
7
|
+
mime-types (>= 1.16)
|
8
|
+
nokogiri (>= 1.3.3)
|
9
|
+
rack (>= 1.0.0)
|
10
|
+
rack-test (>= 0.5.4)
|
11
|
+
selenium-webdriver (>= 0.0.27)
|
12
|
+
xpath (~> 0.1.3)
|
13
|
+
celerity (0.8.8)
|
14
|
+
childprocess (0.1.7)
|
15
|
+
ffi (~> 0.6.3)
|
16
|
+
culerity (0.2.15)
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
ffi (0.6.3)
|
19
|
+
rake (>= 0.8.7)
|
20
|
+
json_pure (1.5.1)
|
21
|
+
mime-types (1.16)
|
22
|
+
nokogiri (1.4.4)
|
23
|
+
rack (1.2.1)
|
24
|
+
rack-test (0.5.7)
|
25
|
+
rack (>= 1.0)
|
26
|
+
rake (0.8.7)
|
27
|
+
rspec (2.5.0)
|
28
|
+
rspec-core (~> 2.5.0)
|
29
|
+
rspec-expectations (~> 2.5.0)
|
30
|
+
rspec-mocks (~> 2.5.0)
|
31
|
+
rspec-core (2.5.1)
|
32
|
+
rspec-expectations (2.5.0)
|
33
|
+
diff-lcs (~> 1.1.2)
|
34
|
+
rspec-mocks (2.5.0)
|
35
|
+
rubyzip (0.9.4)
|
36
|
+
selenium-webdriver (0.1.3)
|
37
|
+
childprocess (~> 0.1.5)
|
38
|
+
ffi (~> 0.6.3)
|
39
|
+
json_pure
|
40
|
+
rubyzip
|
41
|
+
sinatra (1.1.2)
|
42
|
+
rack (~> 1.1)
|
43
|
+
tilt (~> 1.2)
|
44
|
+
tilt (1.2.2)
|
45
|
+
xpath (0.1.3)
|
46
|
+
nokogiri (~> 1.3)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
ruby
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
capybara
|
53
|
+
rake
|
54
|
+
rspec
|
55
|
+
sinatra
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 thoughtbot, inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
capybara-webkit
|
2
|
+
===============
|
3
|
+
|
4
|
+
A [capybara](https://github.com/jnicklas/capybara) driver that uses [WebKit](http://webkit.org) via [QtWebKit](http://doc.qt.nokia.com/4.7/qtwebkit.html).
|
5
|
+
|
6
|
+
Dependencies
|
7
|
+
------------
|
8
|
+
|
9
|
+
capybara-webkit depends on a WebKit implementation from Qt, a cross-platform development toolkit. You'll need to download Qt to build and install the gem.
|
10
|
+
|
11
|
+
* [Download Qt](http://qt.nokia.com/downloads/downloads)
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
Add the capybara-webkit gem to your Gemfile:
|
17
|
+
|
18
|
+
gem "capybara-webkit"
|
19
|
+
|
20
|
+
Set your Capybara Javascript driver to webkit:
|
21
|
+
|
22
|
+
Capybara.javascript_driver = :webkit
|
23
|
+
|
24
|
+
Tag scenarios with @javascript to run them using a headless WebKit browser.
|
25
|
+
|
26
|
+
About
|
27
|
+
-----
|
28
|
+
|
29
|
+
The capybara WebKit driver was written by Joe Ferris, Tristan Dunn, and Jason Morrison from [thoughtbot, inc](http://thoughtbot.com/community).
|
30
|
+
|
31
|
+
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
32
|
+
|
33
|
+
License
|
34
|
+
-------
|
35
|
+
|
36
|
+
capybara-webkit is Copyright © 2011 thoughtbot, inc. It is free software, and may be redistributed under the terms specified in the LICENSE file.
|
37
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
desc "Generate a Makefile using qmake"
|
8
|
+
file 'Makefile' do
|
9
|
+
sh("qmake -spec macx-g++")
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Regenerate dependencies using qmake"
|
13
|
+
task :qmake => 'Makefile' do
|
14
|
+
sh("make qmake")
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Build the webkit server"
|
18
|
+
task :build => :qmake do
|
19
|
+
sh("make")
|
20
|
+
|
21
|
+
FileUtils.mkdir("bin") unless File.directory?("bin")
|
22
|
+
|
23
|
+
if File.exist?("src/webkit_server.app")
|
24
|
+
FileUtils.cp("src/webkit_server.app/Contents/MacOS/webkit_server", "bin")
|
25
|
+
else
|
26
|
+
FileUtils.cp("src/webkit_server", "bin")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
file 'bin/webkit_server' => :build
|
31
|
+
|
32
|
+
unless ENV["BUILD"]
|
33
|
+
RSpec::Core::RakeTask.new do |t|
|
34
|
+
t.pattern = "spec/**/*_spec.rb"
|
35
|
+
t.rspec_opts = "--format progress"
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Default: build and run all specs"
|
39
|
+
task :default => [:build, :spec]
|
40
|
+
|
41
|
+
eval("$specification = begin; #{IO.read('capybara-webkit.gemspec')}; end")
|
42
|
+
Rake::GemPackageTask.new($specification) do |package|
|
43
|
+
package.need_zip = true
|
44
|
+
package.need_tar = true
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Generate a new command called NAME"
|
48
|
+
task :generate_command do
|
49
|
+
name = ENV['NAME'] or raise "Provide a name with NAME="
|
50
|
+
|
51
|
+
header = "src/#{name}.h"
|
52
|
+
source = "src/#{name}.cpp"
|
53
|
+
|
54
|
+
%w(h cpp).each do |extension|
|
55
|
+
File.open("templates/Command.#{extension}", "r") do |source_file|
|
56
|
+
contents = source_file.read
|
57
|
+
contents.gsub!("NAME", name)
|
58
|
+
File.open("src/#{name}.#{extension}", "w") do |target_file|
|
59
|
+
target_file.write(contents)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Dir.glob("src/*.pro").each do |project_file_name|
|
65
|
+
project = IO.read(project_file_name)
|
66
|
+
project.gsub!(/^(HEADERS = .*)/, "\\1 #{name}.h")
|
67
|
+
project.gsub!(/^(SOURCES = .*)/, "\\1 #{name}.cpp")
|
68
|
+
File.open(project_file_name, "w") { |file| file.write(project) }
|
69
|
+
end
|
70
|
+
|
71
|
+
File.open("src/find_command.h", "a") do |file|
|
72
|
+
file.write("CHECK_COMMAND(#{name})")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "capybara-webkit"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.authors = ["thoughtbot", "Joe Ferris", "Jason Morrison", "Tristan Dunn"]
|
5
|
+
s.email = "support@thoughtbot.com"
|
6
|
+
s.files = `git ls-files`.split("\n")
|
7
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
8
|
+
s.homepage = "http://github.com/thoughtbot/capybara-webkit"
|
9
|
+
s.require_path = "lib"
|
10
|
+
s.rubygems_version = "1.3.5"
|
11
|
+
s.summary = "Headless Webkit driver for Capybara"
|
12
|
+
s.add_runtime_dependency "capybara", "~> 0.4.1"
|
13
|
+
s.extensions = "extconf.rb"
|
14
|
+
end
|
15
|
+
|
data/extconf.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require "capybara"
|
2
|
+
require "capybara/driver/webkit/node"
|
3
|
+
require "capybara/driver/webkit/browser"
|
4
|
+
|
5
|
+
class Capybara::Driver::Webkit
|
6
|
+
class WebkitError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :browser
|
10
|
+
|
11
|
+
def initialize(app, options={})
|
12
|
+
@app = app
|
13
|
+
@options = options
|
14
|
+
@rack_server = Capybara::Server.new(@app)
|
15
|
+
@rack_server.boot if Capybara.run_server
|
16
|
+
@browser = options[:browser] || Browser.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_url
|
20
|
+
browser.url
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit(path)
|
24
|
+
browser.visit(url(path))
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(query)
|
28
|
+
browser.find(query).map { |native| Node.new(self, native) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def source
|
32
|
+
browser.source
|
33
|
+
end
|
34
|
+
|
35
|
+
def body
|
36
|
+
source
|
37
|
+
end
|
38
|
+
|
39
|
+
def execute_script(script)
|
40
|
+
browser.execute_script script
|
41
|
+
end
|
42
|
+
|
43
|
+
def evaluate_script(script)
|
44
|
+
browser.evaluate_script script
|
45
|
+
end
|
46
|
+
|
47
|
+
def response_headers
|
48
|
+
raise Capybara::NotSupportedByDriverError
|
49
|
+
end
|
50
|
+
|
51
|
+
def status_code
|
52
|
+
raise Capybara::NotSupportedByDriverError
|
53
|
+
end
|
54
|
+
|
55
|
+
def within_frame(frame_id)
|
56
|
+
raise Capybara::NotSupportedByDriverError
|
57
|
+
end
|
58
|
+
|
59
|
+
def within_window(handle)
|
60
|
+
raise Capybara::NotSupportedByDriverError
|
61
|
+
end
|
62
|
+
|
63
|
+
def wait?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def wait_until(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset!
|
71
|
+
browser.reset!
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_shortcircuit_timeout?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def url(path)
|
81
|
+
@rack_server.url(path)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'capybara/util/timeout'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Capybara::Driver::Webkit
|
6
|
+
class Browser
|
7
|
+
def initialize(options = {})
|
8
|
+
@socket_class = options[:socket_class] || TCPSocket
|
9
|
+
start_server
|
10
|
+
connect
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit(url)
|
14
|
+
command "Visit", url
|
15
|
+
end
|
16
|
+
|
17
|
+
def find(query)
|
18
|
+
command("Find", query).split(",")
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset!
|
22
|
+
command("Reset")
|
23
|
+
end
|
24
|
+
|
25
|
+
def source
|
26
|
+
command("Source")
|
27
|
+
end
|
28
|
+
|
29
|
+
def url
|
30
|
+
command("Url")
|
31
|
+
end
|
32
|
+
|
33
|
+
def command(name, *args)
|
34
|
+
@socket.puts name
|
35
|
+
@socket.puts args.size
|
36
|
+
args.each do |arg|
|
37
|
+
@socket.puts arg.to_s.bytesize
|
38
|
+
@socket.print arg
|
39
|
+
end
|
40
|
+
check
|
41
|
+
read_response
|
42
|
+
end
|
43
|
+
|
44
|
+
def evaluate_script(script)
|
45
|
+
json = command('Evaluate', script)
|
46
|
+
JSON.parse("[#{json}]").first
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute_script(script)
|
50
|
+
command('Execute', script)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def start_server
|
56
|
+
server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
|
57
|
+
@pid = fork { exec(server_path) }
|
58
|
+
at_exit { Process.kill("INT", @pid) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def connect
|
62
|
+
Capybara.timeout(5) do
|
63
|
+
attempt_connect
|
64
|
+
!@socket.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def attempt_connect
|
69
|
+
@socket = @socket_class.open("localhost", 9200)
|
70
|
+
rescue Errno::ECONNREFUSED
|
71
|
+
end
|
72
|
+
|
73
|
+
def check
|
74
|
+
result = @socket.gets.strip
|
75
|
+
|
76
|
+
unless result == 'ok'
|
77
|
+
raise WebkitError, read_response
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_response
|
82
|
+
response_length = @socket.gets.to_i
|
83
|
+
@socket.read(response_length)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class Capybara::Driver::Webkit
|
2
|
+
class Node < Capybara::Driver::Node
|
3
|
+
def text
|
4
|
+
invoke "text"
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](name)
|
8
|
+
value = invoke("attribute", name)
|
9
|
+
if name == 'checked'
|
10
|
+
value == 'true'
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def value
|
17
|
+
if multiple_select?
|
18
|
+
self.find(".//option").select do |option|
|
19
|
+
option["selected"] == "selected"
|
20
|
+
end.map do |option|
|
21
|
+
option.value
|
22
|
+
end
|
23
|
+
else
|
24
|
+
invoke "value"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def set(value)
|
29
|
+
invoke "set", value
|
30
|
+
end
|
31
|
+
|
32
|
+
def select_option
|
33
|
+
invoke "selectOption"
|
34
|
+
end
|
35
|
+
|
36
|
+
def unselect_option
|
37
|
+
invoke "unselectOption"
|
38
|
+
end
|
39
|
+
|
40
|
+
def click
|
41
|
+
invoke "click"
|
42
|
+
end
|
43
|
+
|
44
|
+
def drag_to(element)
|
45
|
+
trigger('mousedown')
|
46
|
+
element.trigger('mousemove')
|
47
|
+
element.trigger('mouseup')
|
48
|
+
end
|
49
|
+
|
50
|
+
def tag_name
|
51
|
+
invoke "tagName"
|
52
|
+
end
|
53
|
+
|
54
|
+
def visible?
|
55
|
+
invoke("visible") == "true"
|
56
|
+
end
|
57
|
+
|
58
|
+
def path
|
59
|
+
raise NotSupportedByDriverError
|
60
|
+
end
|
61
|
+
|
62
|
+
def trigger(event)
|
63
|
+
invoke "trigger", event
|
64
|
+
end
|
65
|
+
|
66
|
+
def find(xpath)
|
67
|
+
invoke("findWithin", xpath).split(',').map do |native|
|
68
|
+
self.class.new(driver, native)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def invoke(name, *args)
|
73
|
+
browser.command "Node", name, native, *args
|
74
|
+
end
|
75
|
+
|
76
|
+
def browser
|
77
|
+
driver.browser
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def multiple_select?
|
83
|
+
self.tag_name == "select" && self["multiple"] == "multiple"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|