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.
Files changed (53) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +55 -0
  5. data/LICENSE +19 -0
  6. data/README.md +37 -0
  7. data/Rakefile +76 -0
  8. data/capybara-webkit.gemspec +15 -0
  9. data/extconf.rb +2 -0
  10. data/lib/capybara-webkit.rb +7 -0
  11. data/lib/capybara/driver/webkit.rb +84 -0
  12. data/lib/capybara/driver/webkit/browser.rb +86 -0
  13. data/lib/capybara/driver/webkit/node.rb +87 -0
  14. data/spec/driver_spec.rb +465 -0
  15. data/spec/integration/driver_spec.rb +21 -0
  16. data/spec/integration/session_spec.rb +12 -0
  17. data/spec/spec_helper.rb +22 -0
  18. data/spec/support/socket_debugger.rb +42 -0
  19. data/src/Command.cpp +15 -0
  20. data/src/Command.h +28 -0
  21. data/src/Connection.cpp +130 -0
  22. data/src/Connection.h +36 -0
  23. data/src/Evaluate.cpp +84 -0
  24. data/src/Evaluate.h +22 -0
  25. data/src/Execute.cpp +17 -0
  26. data/src/Execute.h +12 -0
  27. data/src/Find.cpp +20 -0
  28. data/src/Find.h +13 -0
  29. data/src/JavascriptInvocation.cpp +14 -0
  30. data/src/JavascriptInvocation.h +19 -0
  31. data/src/Node.cpp +14 -0
  32. data/src/Node.h +13 -0
  33. data/src/Reset.cpp +16 -0
  34. data/src/Reset.h +12 -0
  35. data/src/Server.cpp +21 -0
  36. data/src/Server.h +20 -0
  37. data/src/Source.cpp +14 -0
  38. data/src/Source.h +12 -0
  39. data/src/Url.cpp +14 -0
  40. data/src/Url.h +12 -0
  41. data/src/Visit.cpp +20 -0
  42. data/src/Visit.h +16 -0
  43. data/src/WebPage.cpp +81 -0
  44. data/src/WebPage.h +29 -0
  45. data/src/capybara.js +98 -0
  46. data/src/find_command.h +13 -0
  47. data/src/main.cpp +20 -0
  48. data/src/webkit_server.pro +9 -0
  49. data/src/webkit_server.qrc +5 -0
  50. data/templates/Command.cpp +10 -0
  51. data/templates/Command.h +12 -0
  52. data/webkit_server.pro +4 -0
  53. metadata +140 -0
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ *.swp
2
+ bin
3
+ *.swo
4
+ *~
5
+ *.o
6
+ *.moc
7
+ Makefile*
8
+ qrc_*
9
+ *.xcodeproj
10
+ *.app
11
+ moc_*.cpp
12
+ .bundle
13
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+ gem "rake"
3
+ gem "rspec", :require => false
4
+ gem "capybara"
5
+ gem "sinatra", :require => false
6
+
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,2 @@
1
+ system("rake build BUILD=true")
2
+
@@ -0,0 +1,7 @@
1
+ require "capybara"
2
+ require "capybara/driver/webkit"
3
+
4
+ Capybara.register_driver :webkit do |app|
5
+ Capybara::Driver::Webkit.new(app)
6
+ end
7
+
@@ -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
+