steam 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README.textile +91 -0
- data/Rakefile +23 -0
- data/TODO +7 -0
- data/lib/core_ext/ruby/array/flatten_once.rb +9 -0
- data/lib/core_ext/ruby/hash/except.rb +11 -0
- data/lib/core_ext/ruby/hash/slice.rb +14 -0
- data/lib/core_ext/ruby/kernel/silence_warnings.rb +8 -0
- data/lib/core_ext/ruby/process/daemon.rb +23 -0
- data/lib/core_ext/ruby/string/camelize.rb +5 -0
- data/lib/core_ext/ruby/string/underscore.rb +5 -0
- data/lib/steam.rb +43 -0
- data/lib/steam/browser.rb +24 -0
- data/lib/steam/browser/html_unit.rb +87 -0
- data/lib/steam/browser/html_unit/actions.rb +176 -0
- data/lib/steam/browser/html_unit/client.rb +74 -0
- data/lib/steam/browser/html_unit/connection.rb +79 -0
- data/lib/steam/browser/html_unit/drb.rb +45 -0
- data/lib/steam/browser/html_unit/matchers.rb +57 -0
- data/lib/steam/browser/html_unit/page.rb +51 -0
- data/lib/steam/browser/html_unit/web_response.rb +116 -0
- data/lib/steam/connection.rb +9 -0
- data/lib/steam/connection/mock.rb +57 -0
- data/lib/steam/connection/net_http.rb +42 -0
- data/lib/steam/connection/open_uri.rb +24 -0
- data/lib/steam/connection/rails.rb +20 -0
- data/lib/steam/connection/static.rb +33 -0
- data/lib/steam/java.rb +74 -0
- data/lib/steam/process.rb +53 -0
- data/lib/steam/request.rb +49 -0
- data/lib/steam/response.rb +13 -0
- data/lib/steam/session.rb +30 -0
- data/lib/steam/session/rails.rb +33 -0
- data/lib/steam/version.rb +3 -0
- data/test/all.rb +3 -0
- data/test/browser/html_unit/actions_test.rb +183 -0
- data/test/browser/html_unit/javascript_test.rb +60 -0
- data/test/browser/html_unit/rails_actions_test.rb +151 -0
- data/test/browser/html_unit_test.rb +97 -0
- data/test/connection/cascade_test.rb +42 -0
- data/test/connection/mock_test.rb +58 -0
- data/test/connection/rails_test.rb +16 -0
- data/test/connection/static_test.rb +14 -0
- data/test/fixtures/html_fakes.rb +191 -0
- data/test/java_test.rb +29 -0
- data/test/playground/connection.rb +19 -0
- data/test/playground/dragdrop_behavior.rb +60 -0
- data/test/playground/drb.rb +55 -0
- data/test/playground/java_signature.rb +22 -0
- data/test/playground/nokogiri.rb +15 -0
- data/test/playground/put_headers.rb +83 -0
- data/test/playground/rack.rb +11 -0
- data/test/playground/rjb_bind.rb +42 -0
- data/test/playground/stack_level_problem.rb +129 -0
- data/test/playground/thread_problem.rb +57 -0
- data/test/playground/web_response_data.rb +21 -0
- data/test/playground/webrat.rb +48 -0
- data/test/playground/xhr_accept_headers.rb +61 -0
- data/test/process_test.rb +55 -0
- data/test/session_test.rb +15 -0
- data/test/test_helper.rb +56 -0
- metadata +135 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Sven Fuchs <svenfuchs@artweb-design.de>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
<a name="readme"></a>
|
2
|
+
|
3
|
+
h1. Steam
|
4
|
+
|
5
|
+
Steam is a headless integration testing tool driving "HtmlUnit":http://htmlunit.sourceforge.net to enable testing JavaScript-driven web sites. In that it is similar to "Culerity":http://github.com/langalex/culerity which drives "Celerity":http://github.com/jarib/celerity (which also drives "HtmlUnit":http://htmlunit.sourceforge.net). See below for a "comparsion":#comparsion.
|
6
|
+
|
7
|
+
<a name="installation"></a>
|
8
|
+
|
9
|
+
h2. Installation
|
10
|
+
|
11
|
+
Steam currently has the following dependencies:
|
12
|
+
|
13
|
+
* Working Java Runtime
|
14
|
+
* HtmlUnit (jar files)
|
15
|
+
* RJB and Locator gems
|
16
|
+
|
17
|
+
Installing Steam as as a gem will automatically install the required RJB and Locator gems:
|
18
|
+
|
19
|
+
pre. $ gem install steam
|
20
|
+
|
21
|
+
To install HtmlUnit you can download it from "Sourceforge":http://sourceforge.net/projects/htmlunit/files.
|
22
|
+
|
23
|
+
You then need to add HtmlUnit to your Java classpath. The following ways should both work:
|
24
|
+
|
25
|
+
<pre># anywhere during startup, e.g. in features/support/env.rb
|
26
|
+
ENV['CLASSPATH'] = Dir["path/to/your/htmlunit/*.jar"].join(':')
|
27
|
+
|
28
|
+
# after steam has been added to the load path, e.g. in features/support/env.rb
|
29
|
+
Steam.config[:html_unit][:java_path] = 'path/to/your/htmlunit'</pre>
|
30
|
+
|
31
|
+
If you're on Mac OS X then you also need to export the JAVA_HOME variable for RJB. See here for two solutions: "Installing RJB on Mac OS X":http://www.elctech.com/articles/sudo-java_home-and-mac-os-x. The visudo way worked for us. Don't forget to add yourself to the sudoers file, though.
|
32
|
+
|
33
|
+
|
34
|
+
<a name="configuration"></a>
|
35
|
+
|
36
|
+
h2. Configuration
|
37
|
+
|
38
|
+
You should not need to configure anything. If you do need though have a look at "Steam.config":http://github.com/svenfuchs/steam/blob/master/lib/steam.rb
|
39
|
+
|
40
|
+
E.g. in order to tweak the Java load params you can
|
41
|
+
|
42
|
+
pre. Steam.config[:java_load_params] = "-Xmx2048M"
|
43
|
+
|
44
|
+
|
45
|
+
<a name="usage"></a>
|
46
|
+
|
47
|
+
h2. Usage
|
48
|
+
|
49
|
+
You can use Steam by itself as well as with Cucumber. You can find an example for a Cucumber setup in examples/cucumber/env.rb.
|
50
|
+
|
51
|
+
Steam is widely compatible with Webrat - many actions are implemented and take the same or very similar parameters as their Webrat equivalent. You even might be able to use the default webrat_steps.rb that ships with Cucumber, *but his file is meant as an example and might be out of date.*
|
52
|
+
|
53
|
+
|
54
|
+
<a name="demo"></a>
|
55
|
+
|
56
|
+
h2. Demo
|
57
|
+
|
58
|
+
You can find a demo application here: "http://github.com/clemens/steam-demo":http://github.com/clemens/steam-demo
|
59
|
+
|
60
|
+
|
61
|
+
<a name="comparsion"></a>
|
62
|
+
|
63
|
+
h2. Comparsion to others
|
64
|
+
|
65
|
+
Steam's advantages over Culerity/Celerity:
|
66
|
+
|
67
|
+
* runs in Ruby MRI and does not require an entire JRuby environment.
|
68
|
+
* Steam can have the HtmlUnit browser running in the same stack as your tests, thus making the whole thing less complex and hard to debug
|
69
|
+
* Steam does not build on Celerity which is a quite heavy-weight Ruby wrapper around HtmlUnit adding a lot of unnecessary code
|
70
|
+
* Steam uses "Locator":http://github.com/svenfuchs/locator
|
71
|
+
|
72
|
+
Culerity/Celerity's advantages over Steam:
|
73
|
+
|
74
|
+
* RJB can't resolve the mismatch of Ruby vs Java threads which makes fancy setups impossible to solve
|
75
|
+
* Celerity implements a *lot* of stuff, maybe it contains something you need (e.g. maybe you want to test pop-down windows opening in the background?)
|
76
|
+
* Steam still is in its infancy
|
77
|
+
|
78
|
+
|
79
|
+
<a name="acknowledgements"></a>
|
80
|
+
|
81
|
+
h2. Acknowledgements
|
82
|
+
|
83
|
+
Kudos to "Alexander Lang":http://github.com/langalex for writing "Culerity":http://github.com/langalex/culerity which pioneered full-stack AJAX-enabled integration testing in Rails.
|
84
|
+
|
85
|
+
|
86
|
+
<a name="developers"></a>
|
87
|
+
|
88
|
+
h2. Developers
|
89
|
+
|
90
|
+
* "Sven Fuchs":http://github.com/svenfuchs
|
91
|
+
* "Clemens Kofler"::http://github.com/clemens
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$: << File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'steam/version'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |s|
|
9
|
+
s.name = 'steam'
|
10
|
+
s.version = Steam::VERSION
|
11
|
+
s.summary = 'Headless integration testing w/ HtmlUnit: enables testing JavaScript-driven web sites '
|
12
|
+
s.email = 'svenfuchs@artweb-design.de'
|
13
|
+
s.homepage = 'http://github.com/svenfuchs/steam'
|
14
|
+
s.description = 'Steam is a headless integration testing tool driving HtmlUnit to enable testing JavaScript-driven web sites.'
|
15
|
+
s.authors = ['Sven Fuchs', 'Clemens Kofler']
|
16
|
+
s.files = FileList['[A-Z]*', 'lib/steam.rb', 'lib/{core_ext,steam}/**/*']
|
17
|
+
|
18
|
+
s.add_dependency 'rjb', '>= 1.2.0'
|
19
|
+
s.add_dependency 'locator', '>= 0.0.4'
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts 'Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com'
|
23
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
http://htmlunit.sourceforge.net/logging.html
|
2
|
+
|
3
|
+
http://htmlunit.sourceforge.net/faq.html#MemoryLeak
|
4
|
+
HtmlUnit appears to be leaking memory; what's the deal?
|
5
|
+
Make sure (a) that you are using the latest version of HtmlUnit, and (b) that you are calling WebClient.closeAllWindows() when you are finished with your WebClient instance.
|
6
|
+
|
7
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Hash
|
2
|
+
def slice!(*keys)
|
3
|
+
omit = slice(*self.keys - keys)
|
4
|
+
hash = slice(*keys)
|
5
|
+
replace(hash)
|
6
|
+
omit
|
7
|
+
end
|
8
|
+
|
9
|
+
def slice(*keys)
|
10
|
+
hash = self.class.new
|
11
|
+
keys.each { |k| hash[k] = self[k] if has_key?(k) }
|
12
|
+
hash
|
13
|
+
end
|
14
|
+
end unless Hash.method_defined?(:slice)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Process
|
2
|
+
def self.daemon(nochdir = nil, noclose = nil)
|
3
|
+
exit if fork # Parent exits, child continues.
|
4
|
+
Process.setsid # Become session leader.
|
5
|
+
exit if fork # Zap session leader. See [1].
|
6
|
+
|
7
|
+
unless nochdir
|
8
|
+
Dir.chdir "/" # Release old working directory.
|
9
|
+
end
|
10
|
+
|
11
|
+
File.umask 0000 # Ensure sensible umask. Adjust as needed.
|
12
|
+
|
13
|
+
unless noclose
|
14
|
+
STDIN.reopen "/dev/null" # Free file descriptors and
|
15
|
+
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
|
16
|
+
STDERR.reopen '/dev/null', 'a'
|
17
|
+
end
|
18
|
+
|
19
|
+
trap("TERM") { exit }
|
20
|
+
|
21
|
+
return 0
|
22
|
+
end unless respond_to?(:daemon)
|
23
|
+
end
|
data/lib/steam.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Steam
|
4
|
+
autoload :Browser, 'steam/browser'
|
5
|
+
autoload :Connection, 'steam/connection'
|
6
|
+
autoload :Java, 'steam/java'
|
7
|
+
autoload :Process, 'steam/process'
|
8
|
+
autoload :Request, 'steam/request'
|
9
|
+
autoload :Response, 'steam/response'
|
10
|
+
autoload :Session, 'steam/session'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def config
|
14
|
+
@@config ||= {
|
15
|
+
:request_url => 'http://localhost',
|
16
|
+
:server_name => 'localhost',
|
17
|
+
:server_port => '3000',
|
18
|
+
:url_scheme => 'http',
|
19
|
+
:charset => 'utf-8',
|
20
|
+
:java_load_params => '-Xmx1024M',
|
21
|
+
:drb_uri => 'druby://127.0.0.1:9000',
|
22
|
+
:html_unit => {
|
23
|
+
:java_path => File.expand_path("../../vendor/htmlunit-2.6/", __FILE__),
|
24
|
+
:browser_version => :FIREFOX_3,
|
25
|
+
:css => true,
|
26
|
+
:javascript => true,
|
27
|
+
:resynchronize => true,
|
28
|
+
:js_timeout => 5000,
|
29
|
+
:log_level => :warning,
|
30
|
+
:log_incorrectness => false,
|
31
|
+
:on_error_status => nil, # set to :raise to raise an exception on error status, :print to print content
|
32
|
+
:on_script_error => nil # set to :raise to raise an exception on javascript exceptions
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ElementNotFound < StandardError
|
39
|
+
def initialize(*args)
|
40
|
+
super "could not find element: #{args.map { |arg| arg.inspect }.join(', ') }"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# We currently only implement HtmlUnit as a browser. Maybe at some point
|
2
|
+
# Webdriver might be an interesting alternative.
|
3
|
+
|
4
|
+
require 'core_ext/ruby/string/camelize'
|
5
|
+
|
6
|
+
module Steam
|
7
|
+
module Browser
|
8
|
+
autoload :HtmlUnit, 'steam/browser/html_unit'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def create(*args)
|
12
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
13
|
+
type = args.shift if args.first.is_a?(Symbol)
|
14
|
+
connection = args.pop
|
15
|
+
|
16
|
+
type ||= :html_unit
|
17
|
+
type = const_get(type.to_s.camelize)
|
18
|
+
type = type.const_get('Drb') if options[:daemon]
|
19
|
+
|
20
|
+
type.new(connection, :daemon => true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Browser implementation using HtmlUnit. Steam::Session delegates here, so this
|
2
|
+
# interface is available in your Cucumber environment. Also see HtmlUnit::Actions
|
3
|
+
# which is included here.
|
4
|
+
|
5
|
+
require 'locator'
|
6
|
+
require 'core_ext/ruby/kernel/silence_warnings'
|
7
|
+
|
8
|
+
module Steam
|
9
|
+
module Browser
|
10
|
+
class HtmlUnit
|
11
|
+
autoload :Actions, 'steam/browser/html_unit/actions'
|
12
|
+
autoload :Client, 'steam/browser/html_unit/client'
|
13
|
+
autoload :Drb, 'steam/browser/html_unit/drb'
|
14
|
+
autoload :Forker, 'steam/browser/html_unit/forker'
|
15
|
+
autoload :Connection, 'steam/browser/html_unit/connection'
|
16
|
+
autoload :Matchers, 'steam/browser/html_unit/matchers'
|
17
|
+
autoload :Page, 'steam/browser/html_unit/page'
|
18
|
+
autoload :WebResponse, 'steam/browser/html_unit/web_response'
|
19
|
+
|
20
|
+
include Actions # Matchers
|
21
|
+
|
22
|
+
attr_accessor :client, :page, :connection, :request, :response
|
23
|
+
|
24
|
+
def initialize(*args)
|
25
|
+
@client = Client.new(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def close
|
29
|
+
@client.closeAllWindows
|
30
|
+
end
|
31
|
+
|
32
|
+
def request(url)
|
33
|
+
call Request.env_for(url)
|
34
|
+
end
|
35
|
+
alias :visit :request
|
36
|
+
|
37
|
+
def call(env)
|
38
|
+
respond_to do
|
39
|
+
@request = Rack::Request.new(env)
|
40
|
+
client.request(@request.url)
|
41
|
+
end.to_a
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute(javascript)
|
45
|
+
page.execute(javascript) # FIXME does execute return a page so we need to respond?
|
46
|
+
end
|
47
|
+
|
48
|
+
def locate(*args, &block)
|
49
|
+
Locator.locate(dom, *args, &block) || raise(ElementNotFound.new(*args))
|
50
|
+
end
|
51
|
+
|
52
|
+
def locate_in_browser(*args, &block)
|
53
|
+
if args.first.respond_to?(:_classname) # native HtmlUnit element
|
54
|
+
args.first
|
55
|
+
elsif args.first.respond_to?(:xpath) # Locator element
|
56
|
+
silence_warnings { page.getFirstByXPath(args.first.xpath) }
|
57
|
+
else
|
58
|
+
locate_in_browser(locate(*args, &block)) # something else
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def within(*args, &block)
|
63
|
+
Locator.within(*args, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def respond_to
|
69
|
+
result = yield || raise('Block did not yield a dom.gargoylesoftware.htmlunit.html.HtmlPage.')
|
70
|
+
@page = Page.new(result)
|
71
|
+
client.wait_for_javascript(Steam.config[:html_unit][:js_timeout])
|
72
|
+
@response = Response.new(*page.to_a)
|
73
|
+
end
|
74
|
+
|
75
|
+
def dom
|
76
|
+
case Locator::Dom.adapter.name # yuck
|
77
|
+
when /Nokogiri/
|
78
|
+
response.body
|
79
|
+
when /Htmlunit/
|
80
|
+
@page.page
|
81
|
+
else
|
82
|
+
raise 'incompatible Locator::Dom adapter'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# Convenience dsl for executing typical actions on the web browser and current
|
2
|
+
# page. Mimicks the well-known Webrat API but adds stuff like drag/drop support.
|
3
|
+
|
4
|
+
require 'core_ext/ruby/hash/slice'
|
5
|
+
|
6
|
+
module Steam
|
7
|
+
module Browser
|
8
|
+
class HtmlUnit
|
9
|
+
module Actions
|
10
|
+
def click_on(*args)
|
11
|
+
respond_to { locate_in_browser(*args).click }
|
12
|
+
end
|
13
|
+
|
14
|
+
def click_link(element, options = {})
|
15
|
+
respond_to { locate_in_browser(:link, element, options).click }
|
16
|
+
end
|
17
|
+
|
18
|
+
def click_button(element, options = {})
|
19
|
+
respond_to { locate_in_browser(:button, element, options).click }
|
20
|
+
end
|
21
|
+
|
22
|
+
def click_area(element, options = {})
|
23
|
+
respond_to { locate_in_browser(:area, element, options).click }
|
24
|
+
end
|
25
|
+
|
26
|
+
def fill_in(element, options = {})
|
27
|
+
respond_to do
|
28
|
+
value = options.delete(:with)
|
29
|
+
element = locate_in_browser(:field, element, options)
|
30
|
+
result = element.setText(value) rescue element.setValueAttribute(value)
|
31
|
+
# TODO - submit a bug: element.setText returns nil, textarea.setValueAttribute returns a page
|
32
|
+
result || page
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def check(element, options = {})
|
37
|
+
respond_to { locate_in_browser(:check_box, element, options).setChecked(true) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def uncheck(element, options = {})
|
41
|
+
respond_to { locate_in_browser(:check_box, element, options).setChecked(false) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def choose(element, options = {})
|
45
|
+
respond_to { locate_in_browser(:radio_button, element, options).setChecked(true) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def select(element, options = {})
|
49
|
+
options.update(:within => [:select, options.delete(:from)])
|
50
|
+
respond_to { locate_in_browser(:select_option, element, options).setSelected(true) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_hidden_field(element, options = {})
|
54
|
+
respond_to do
|
55
|
+
value = options.delete(:to)
|
56
|
+
locate_in_browser(:hidden_field, element, options).setValueAttribute(value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO implement a way to supply content_type
|
61
|
+
def attach_file(element, path, options = {})
|
62
|
+
fill_in(element, options.merge(:with => path))
|
63
|
+
end
|
64
|
+
|
65
|
+
def submit_form(element, options = {})
|
66
|
+
respond_to do
|
67
|
+
scope = [:form, element, options]
|
68
|
+
locate_in_browser(:input, :type => 'submit', :within => scope).click
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def drag_and_drop(element, options = {})
|
73
|
+
drag(element, options)
|
74
|
+
drop
|
75
|
+
end
|
76
|
+
|
77
|
+
def drag(element, options = {})
|
78
|
+
respond_to do
|
79
|
+
target = extract_drop_target!(options, [:to, :onto, :over, :target])
|
80
|
+
element = locate_in_browser(element, options)
|
81
|
+
if target
|
82
|
+
element.mouseDown
|
83
|
+
@_drop_target = locate_in_browser(target)
|
84
|
+
@_drop_target.mouseMove
|
85
|
+
else
|
86
|
+
element.mouseDown
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def drop(element = nil, options = {})
|
92
|
+
respond_to do
|
93
|
+
element = @_drop_target || locate_in_browser(element, options)
|
94
|
+
element.mouseMove unless @_drop_target
|
95
|
+
@_drop_target = nil
|
96
|
+
element.mouseUp
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def hover(element, options = {})
|
101
|
+
respond_to { locate_in_browser(element, options).mouseOver }
|
102
|
+
end
|
103
|
+
|
104
|
+
def blur(element, options = {})
|
105
|
+
respond_to do
|
106
|
+
locate_in_browser(element, options).blur
|
107
|
+
@page # blur seems to return nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def focus(element, options = {})
|
112
|
+
respond_to do
|
113
|
+
locate_in_browser(element, options).focus
|
114
|
+
@page # focus seems to return nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def double_click(element, options = {})
|
119
|
+
respond_to { locate_in_browser(element, options).dblClick }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Rails specific respond_tos
|
123
|
+
DATE_TIME_CODE = {
|
124
|
+
:year => '1i',
|
125
|
+
:month => '2i',
|
126
|
+
:day => '3i',
|
127
|
+
:hour => '4i',
|
128
|
+
:minute => '5i',
|
129
|
+
:second => '6i'
|
130
|
+
}
|
131
|
+
|
132
|
+
def select_date(date, options = {})
|
133
|
+
date = date.respond_to?(:strftime) ? date : Date.parse(date)
|
134
|
+
prefix = locate_id_prefix(options)
|
135
|
+
|
136
|
+
# FIXME .to_s chould be done somewhere inside the locator
|
137
|
+
select(date.year.to_s, :from => "#{prefix}_#{DATE_TIME_CODE[:year]}")
|
138
|
+
select(date.strftime('%B'), :from => "#{prefix}_#{DATE_TIME_CODE[:month]}")
|
139
|
+
select(date.day.to_s, :from => "#{prefix}_#{DATE_TIME_CODE[:day]}")
|
140
|
+
end
|
141
|
+
|
142
|
+
def select_datetime(datetime, options = {})
|
143
|
+
select_date(datetime)
|
144
|
+
select_time(datetime)
|
145
|
+
end
|
146
|
+
|
147
|
+
def select_time(time, options = {})
|
148
|
+
time = time.respond_to?(:strftime) ? time : DateTime.parse(time)
|
149
|
+
prefix = locate_id_prefix(options)
|
150
|
+
|
151
|
+
select(time.hour.to_s.rjust(2,'0'), :from => "#{prefix}_#{DATE_TIME_CODE[:hour]}")
|
152
|
+
select(time.min.to_s.rjust(2,'0'), :from => "#{prefix}_#{DATE_TIME_CODE[:minute]}")
|
153
|
+
# second?
|
154
|
+
end
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
# inspired by Webrat
|
159
|
+
def locate_id_prefix(options)
|
160
|
+
options[:id_prefix] ? options[:id_prefix] : locate_id_prefix_label(options).attribute('for')
|
161
|
+
end
|
162
|
+
|
163
|
+
def locate_id_prefix_label(options)
|
164
|
+
locate_in_browser(:label, options[:from]) rescue locate_in_browser(:label, :for => options[:from])
|
165
|
+
end
|
166
|
+
|
167
|
+
def extract_drop_target!(targets, keys)
|
168
|
+
rest = targets.slice!(*keys)
|
169
|
+
target = targets.values.compact.first
|
170
|
+
targets.replace(rest)
|
171
|
+
target
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|