steam 0.0.2
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/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
|