Permalink
Browse files

omg

  • Loading branch information...
0 parents commit 76fe32debda465cb68a4f142351e0e6b876f3055 @jonleighton jonleighton committed Oct 27, 2011
Showing with 2,057 additions and 0 deletions.
  1. 0 CHANGELOG.md
  2. +7 −0 Gemfile
  3. +63 −0 Gemfile.lock
  4. +20 −0 LICENSE
  5. +87 −0 README.md
  6. +5 −0 Rakefile
  7. +20 −0 lib/capybara/poltergeist.rb
  8. +130 −0 lib/capybara/poltergeist/browser.rb
  9. +57 −0 lib/capybara/poltergeist/client.rb
  10. +175 −0 lib/capybara/poltergeist/client/agent.coffee
  11. +121 −0 lib/capybara/poltergeist/client/browser.coffee
  12. +198 −0 lib/capybara/poltergeist/client/compiled/agent.js
  13. +117 −0 lib/capybara/poltergeist/client/compiled/browser.js
  14. +20 −0 lib/capybara/poltergeist/client/compiled/connection.js
  15. +38 −0 lib/capybara/poltergeist/client/compiled/main.js
  16. +74 −0 lib/capybara/poltergeist/client/compiled/node.js
  17. +136 −0 lib/capybara/poltergeist/client/compiled/web_page.js
  18. +11 −0 lib/capybara/poltergeist/client/connection.coffee
  19. +27 −0 lib/capybara/poltergeist/client/main.coffee
  20. +56 −0 lib/capybara/poltergeist/client/node.coffee
  21. +102 −0 lib/capybara/poltergeist/client/web_page.coffee
  22. +85 −0 lib/capybara/poltergeist/driver.rb
  23. +26 −0 lib/capybara/poltergeist/errors.rb
  24. +92 −0 lib/capybara/poltergeist/node.rb
  25. +35 −0 lib/capybara/poltergeist/server.rb
  26. +118 −0 lib/capybara/poltergeist/server_manager.rb
  27. +5 −0 lib/capybara/poltergeist/version.rb
  28. +23 −0 poltergeist.gemspec
  29. +30 −0 spec/integration/driver_spec.rb
  30. +49 −0 spec/integration/session_spec.rb
  31. +32 −0 spec/spec_helper.rb
  32. +9 −0 spec/support/public/test.js
  33. +14 −0 spec/support/test_app.rb
  34. +17 −0 spec/support/views/with_js.erb
  35. +29 −0 spec/unit/browser_spec.rb
  36. +29 −0 spec/unit/driver_spec.rb
No changes.
@@ -0,0 +1,7 @@
+source :rubygems
+
+gemspec
+
+gem 'rspec'
+gem 'sinatra'
+gem 'json'
@@ -0,0 +1,63 @@
+PATH
+ remote: .
+ specs:
+ poltergeist (0.1.0)
+ capybara (~> 1.1.0)
+ em-websocket (~> 0.3.1)
+ json (~> 1.6)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ addressable (2.2.6)
+ capybara (1.1.1)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (~> 2.0)
+ xpath (~> 0.1.4)
+ childprocess (0.2.2)
+ ffi (~> 1.0.6)
+ diff-lcs (1.1.2)
+ em-websocket (0.3.1)
+ addressable (>= 2.1.1)
+ eventmachine (>= 0.12.9)
+ eventmachine (0.12.10)
+ ffi (1.0.9)
+ json (1.6.1)
+ json_pure (1.6.1)
+ mime-types (1.16)
+ nokogiri (1.5.0)
+ rack (1.3.3)
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ rspec (2.3.0)
+ rspec-core (~> 2.3.0)
+ rspec-expectations (~> 2.3.0)
+ rspec-mocks (~> 2.3.0)
+ rspec-core (2.3.1)
+ rspec-expectations (2.3.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.3.0)
+ rubyzip (0.9.4)
+ selenium-webdriver (2.8.0)
+ childprocess (>= 0.2.1)
+ ffi (>= 1.0.7)
+ json_pure
+ rubyzip
+ sinatra (1.2.6)
+ rack (~> 1.1)
+ tilt (>= 1.2.2, < 2.0)
+ tilt (1.3.3)
+ xpath (0.1.4)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ json
+ poltergeist!
+ rspec
+ sinatra
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Jonathan Leighton
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
+# Poltergeist - A PhantomJS driver for Capybara #
+
+Version: 0.1.0
+
+Poltergeist is a driver for [Capybara](https://github.com/jnicklas/capybara). It allows you to
+run your Capybara tests on a headless [WebKit](http://webkit.org) browser,
+provided by [PhantomJS](http://www.phantomjs.org/).
+
+## Installation ##
+
+Add `poltergeist` to your Gemfile, and add in your test setup add:
+
+ require 'capybara/poltergeist'
+ Capybara.javascript_driver = :poltergeist
+
+You will also need PhantomJS 1.3+ on your system.
+[Here's how to do that](http://code.google.com/p/phantomjs/wiki/BuildInstructions).
+
+Currently PhantomJS is not 'truly headless', so to run it on a continuous integration
+server you will need to use [Xvfb](http://en.wikipedia.org/wiki/Xvfb). You can either use the
+[headless gem](https://github.com/leonid-shevtsov/headless) for this,
+or make sure that Xvfb is running and the `DISPLAY` environment variable is set.
+
+## What's supported? ##
+
+Poltergeist supports basically everything that is supported by the stock Selenium driver,
+including Javascript, drag-and-drop, etc.
+
+Additionally, you can grab screenshots of the page at any point by calling
+`page.driver.render('/path/to/file.png')` (this works the same way as the PhantomJS
+render feature, so you can specify other extensions like `.pdf`, `.gif`, etc.)
+
+## Customization ##
+
+You can customize the way that Capybara sets up Poltegeist via the following code in your
+test setup:
+
+ Capybara.register_driver :poltergeist do |app|
+ Capybara::Poltergeist::Driver.new(app, options)
+ end
+
+`options` is a hash of options. The following options are supported:
+
+ * `:phantomjs` (String) - A custom path to the phantomjs executable
+ * `:debug` (Boolean) - When true, debug output is logged to `STDERR`
+ * `:logger` (Object responding to `puts`) - When present, debug output is written to this object
+
+## Bugs ##
+
+Please file bug reports on Github and include example code to reproduce the problem wherever
+possible. (Tests are even better.)
+
+## Why not use [capybara-webkit](https://github.com/thoughtbot/capybara-webkit)? ##
+
+If capybara-webkit works for you, then by all means carry on using it.
+
+However, I have had some trouble with it, and Poltergeist basically started
+as an experiment to see whether a PhantomJS driver was possible. (It turned out it
+was, but only thanks to some new features in the recent 1.3.0 release.)
+
+In the long term, I think having a PhantomJS driver makes sense, because that allows
+PhantomJS to concentrate on being an awesome headless browser, while the capybara driver
+(Poltergeist) is able to be the minimal amount of glue code necessary to drive the
+browser.
+
+## License ##
+
+Copyright (c) 2011 Jonathan Leighton
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
+task :autocompile do
+ system "coffee --compile --bare --watch " \
+ "--output lib/capybara/poltergeist/client/compiled " \
+ "lib/capybara/poltergeist/client/*.coffee"
+end
@@ -0,0 +1,20 @@
+require 'capybara'
+
+module Capybara
+ module Poltergeist
+ autoload :Driver, 'capybara/poltergeist/driver'
+ autoload :Browser, 'capybara/poltergeist/browser'
+ autoload :Node, 'capybara/poltergeist/node'
+ autoload :ServerManager, 'capybara/poltergeist/server_manager'
+ autoload :Server, 'capybara/poltergeist/server'
+ autoload :Client, 'capybara/poltergeist/client'
+
+ autoload :Error, 'capybara/poltergeist/errors'
+ autoload :BrowserError, 'capybara/poltergeist/errors'
+ autoload :ObsoleteNode, 'capybara/poltergeist/errors'
+ end
+end
+
+Capybara.register_driver :poltergeist do |app|
+ Capybara::Poltergeist::Driver.new(app)
+end
@@ -0,0 +1,130 @@
+require 'json'
+
+module Capybara::Poltergeist
+ class Browser
+ attr_reader :options, :server, :client
+
+ def initialize(options = {})
+ @options = options
+ @server = Server.new
+ @client = Client.new(server.port, options[:phantomjs])
+ end
+
+ def restart
+ server.restart
+ client.restart
+ end
+
+ def visit(url, attributes = {})
+ command 'visit', url
+ end
+
+ def current_url
+ command 'current_url'
+ end
+
+ def body
+ command 'body'
+ end
+
+ def source
+ command 'source'
+ end
+
+ def find(selector, id = nil)
+ command 'find', selector, id
+ end
+
+ def text(id)
+ command 'text', id
+ end
+
+ def attribute(id, name)
+ command 'attribute', id, name
+ end
+
+ def value(id)
+ command 'value', id
+ end
+
+ def set(id, value)
+ command 'set', id, value
+ end
+
+ def select_file(id, value)
+ command 'select_file', id, value
+ end
+
+ def tag_name(id)
+ command('tag_name', id).downcase
+ end
+
+ def visible?(id)
+ command 'visible', id
+ end
+
+ def evaluate(script)
+ command 'evaluate', script
+ end
+
+ def execute(script)
+ command 'execute', script
+ end
+
+ def within_frame(id, &block)
+ command 'push_frame', id
+ yield
+ command 'pop_frame'
+ end
+
+ def reset
+ visit('about:blank')
+ end
+
+ def click(id)
+ command 'click', id
+ end
+
+ def drag(id, other_id)
+ command 'drag', id, other_id
+ end
+
+ def select(id, value)
+ command 'select', id, value
+ end
+
+ def trigger(id, event)
+ command 'trigger', id, event
+ end
+
+ def reset
+ command 'reset'
+ end
+
+ def render(path)
+ command 'render', path
+ end
+
+ def logger
+ options[:logger]
+ end
+
+ def log(message)
+ logger.puts message if logger
+ end
+
+ def command(name, *args)
+ message = { 'name' => name, 'args' => args }
+ log message.inspect
+
+ json = JSON.parse(server.send(JSON.generate(message)))
+ log json.inspect
+
+ if json['error']
+ raise BrowserError.new(json['error'])
+ else
+ json['response']
+ end
+ end
+ end
+end
@@ -0,0 +1,57 @@
+require 'open3'
+
+module Capybara::Poltergeist
+ class Client
+ PHANTOM_SCRIPT = File.expand_path('../client/compiled/main.js', __FILE__)
+
+ attr_reader :pid, :port, :path
+
+ def initialize(port, path = nil)
+ @port = port
+ @path = path || 'phantomjs'
+
+ start
+ at_exit { stop }
+ end
+
+ def start
+ @pid = Process.fork do
+ Open3.popen3("#{path} #{PHANTOM_SCRIPT} #{port}") do |stdin, stdout, stderr|
+ loop do
+ select = IO.select([stdout, stderr])
+ stream = select.first.first
+
+ break if stream.eof?
+
+ if stream == stdout
+ STDOUT.puts stdout.readline
+ elsif stream == stderr
+ line = stderr.readline
+
+ # QtWebkit seems to throw this error all the time when using WebSockets, but
+ # it doesn't appear to actually stop anything working, so filter it out.
+ #
+ # This isn't the nicest solution I know :( Hopefully it will be fixed in
+ # QtWebkit (if you search for this string, you'll see it's been reported in
+ # various places).
+ unless line.include?('WebCore::SocketStreamHandlePrivate::socketSentData()')
+ STDERR.puts line
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def stop
+ Process.kill('TERM', pid)
+ rescue Errno::ESRCH
+ # Bovvered, I ain't
+ end
+
+ def restart
+ stop
+ start
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 76fe32d

Please sign in to comment.