revisor_client 0.0.1

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/README.md ADDED
@@ -0,0 +1,24 @@
1
+ Ruby Revisor Client Library
2
+ ===================
3
+
4
+ This library provides object-oriented interface for executing commands
5
+ on [Revisor](http://github.com/sotakone/revisor) server. Althrough you
6
+ can send commands with any HTTP client, this library might save you
7
+ some time collecting command arguments and handling server responses
8
+ for you. It also includes some examples and test suite.
9
+
10
+ Location of Revisor executable
11
+ ----------
12
+
13
+ Client library expects your Revisor executable will be in PATH, or
14
+ it's location is set in REVISOR_PATH environment variable.
15
+
16
+ Running examples (GMail test)
17
+ ----------
18
+
19
+ $ rake examples REVISOR_PATH=/path/to/your/revisor/executable
20
+
21
+ Running tests
22
+ ----------
23
+
24
+ $ rake test REVISOR_PATH=/path/to/your/revisor/executable
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake/testtask'
2
+
3
+ begin
4
+ require 'jeweler'
5
+
6
+ Jeweler::Tasks.new do |gemspec|
7
+ gemspec.name = "revisor_client"
8
+ gemspec.summary = "Ruby helper library for Revisor browser"
9
+ gemspec.description = "Object-oriented interface for remotelly-controller WebKit-based browser named Revisor."
10
+ gemspec.email = "sotakone@sotakone.com"
11
+ gemspec.homepage = "http://github.com/sotakone/revisor/"
12
+ gemspec.description = ""
13
+ gemspec.authors = ["Mikhail Lapshin"]
14
+ end
15
+
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.test_files = FileList['test/*_test.rb']
23
+ end
24
+
25
+ Rake::TestTask.new(:examples) do |t|
26
+ t.test_files = FileList['examples/*_example.rb']
27
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,53 @@
1
+ module Revisor
2
+ class Client
3
+ class Session
4
+ attr_reader :client, :name
5
+
6
+ def initialize(client, name)
7
+ @client = client
8
+ @name = name
9
+ @tabs = {}
10
+ end
11
+
12
+ def stop
13
+ @client.stop_session(self.name)
14
+ end
15
+
16
+ def create_tab(tab_name)
17
+ @client.command("session.tab.create",
18
+ :session_name => @name,
19
+ :tab_name => tab_name)
20
+
21
+ @tabs[tab_name] = Tab.new(self, tab_name)
22
+ end
23
+
24
+ def destroy_tab(tab_name)
25
+ @client.command("session.tab.destroy",
26
+ :session_name => @name,
27
+ :tab_name => tab_name)
28
+
29
+ @tabs.delete(tab_name)
30
+ end
31
+
32
+ def set_cookies(cookies, url)
33
+ cookies.each do |cookie|
34
+ if cookie[:expires_at] && cookie[:expires_at].is_a?(Time)
35
+ cookie[:expires_at] = Client.datetime_to_json(cookie[:expires_at])
36
+ end
37
+ end
38
+
39
+ @client.command("session.set_cookies",
40
+ :session_name => @name,
41
+ :cookies => cookies,
42
+ :url => url)
43
+ end
44
+
45
+ def get_cookies(url)
46
+ @client.command("session.get_cookies",
47
+ :session_name => @name,
48
+ :url => url)[:cookies]
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,93 @@
1
+ module Revisor
2
+ class Client
3
+ class Tab
4
+
5
+ attr_reader :session, :name, :client
6
+
7
+ def initialize(session, name)
8
+ @client = session.client
9
+ @session = session
10
+ @name = name
11
+ end
12
+
13
+ def destroy
14
+ @session.destroy_tab(self.name)
15
+ @session = nil
16
+
17
+ true
18
+ end
19
+
20
+ def visit(url)
21
+ cmd = session_and_tab_names.merge({ :url => url })
22
+ @client.command("session.tab.visit", cmd)
23
+ end
24
+
25
+ def wait_for_load(timeout = 0)
26
+ cmd = session_and_tab_names.merge({ :timeout => timeout })
27
+ @client.command("session.tab.wait_for_load", cmd)
28
+ end
29
+
30
+ def wait_for_condition(script, interval, tries_count)
31
+ successfull = false
32
+ step = 0
33
+
34
+ while !successfull && step < tries_count
35
+ result = e(script)
36
+ puts "WTE: #{result.inspect}"
37
+ successfull = (result == true)
38
+ step = step + 1
39
+ sleep(interval / 1000.0)
40
+ end
41
+
42
+ successfull
43
+ end
44
+
45
+ def set_confirm_answer(answer)
46
+ cmd = session_and_tab_names.merge({ :answer => answer })
47
+ @client.command("session.tab.set_confirm_answer", cmd)
48
+ end
49
+
50
+ def set_prompt_answer(answer, cancelled)
51
+ cmd = session_and_tab_names.merge({ :answer => answer, :cancelled => cancelled })
52
+ @client.command("session.tab.set_prompt_answer", cmd)
53
+ end
54
+
55
+ def evaluate_javascript(script)
56
+ puts "EEE: #{script}"
57
+ cmd = session_and_tab_names.merge({ :script => script })
58
+ @client.command("session.tab.evaluate_javascript", cmd)[:eval_result]
59
+ end
60
+
61
+ alias :e :evaluate_javascript
62
+
63
+ def save_screenshot(file_name, size = nil)
64
+ cmd = session_and_tab_names.merge({ :file_name => file_name })
65
+
66
+ if size
67
+ cmd[:viewport_width] = size.first.to_i
68
+ cmd[:viewport_height] = size.last.to_i
69
+ end
70
+
71
+ @client.command("session.tab.save_screenshot", cmd)
72
+ end
73
+
74
+ def send_mouse_event(type, x, y, options = {})
75
+ options[:button] ||= "left" if type == "click" || type == "dblclick"
76
+ cmd = session_and_tab_names.merge(:x => x, :y => y, :type => type).merge(options)
77
+ @client.command("session.tab.send_mouse_event", cmd)
78
+ end
79
+
80
+ def send_key_event(type, key, text, options = {})
81
+ cmd = session_and_tab_names.merge(:text => text, :key => key, :type => type).merge(options)
82
+ @client.command("session.tab.send_key_event", cmd)
83
+ end
84
+
85
+ protected
86
+
87
+ def session_and_tab_names
88
+ { :session_name => session.name, :tab_name => name }
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'net/http'
4
+ require File.join(File.dirname(__FILE__), "client", "session")
5
+ require File.join(File.dirname(__FILE__), "client", "tab")
6
+
7
+ module Revisor
8
+ class Client
9
+
10
+ class ServerError < RuntimeError
11
+ end
12
+
13
+ attr_accessor :host, :port
14
+
15
+ def initialize(host, port)
16
+ @host = host
17
+ @port = port
18
+
19
+ @sessions = {}
20
+ end
21
+
22
+ def command(command_name, params = {})
23
+ command = { :name => command_name }.merge(params)
24
+ json = JSON.generate(command)
25
+ # puts "==> " + json
26
+
27
+ response = Net::HTTP.post_form(URI.parse("http://#{@host}:#{@port}/command"), 'command' => json)
28
+ # puts "<== " + response.body
29
+
30
+ response_json = JSON.parse(response.body).inject({}) { |r, k| r[k.first.to_sym] = k.last; r }
31
+
32
+ if response_json[:result] == 'Error'
33
+ raise ServerError, response_json[:message]
34
+ end
35
+
36
+ response_json
37
+ end
38
+
39
+ def start_session(name)
40
+ command("session.start", :session_name => name)
41
+ @sessions[name] = Session.new(self, name)
42
+ end
43
+
44
+ def stop_session(name)
45
+ command("session.stop", :session_name => name)
46
+ @sessions.delete(name)
47
+ end
48
+
49
+ def session(name)
50
+ if @sessions.include?(name)
51
+ @sessions[name]
52
+ else
53
+ raise "No session found with name #{name}"
54
+ end
55
+ end
56
+
57
+ def [] (n)
58
+ session(n)
59
+ end
60
+
61
+ def self.datetime_to_json(t)
62
+ t.utc.strftime("%Y-%m-%dT%H:%M:%S")
63
+ end
64
+
65
+ end
66
+ end
data/lib/revisor.rb ADDED
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), "revisor", "client")
2
+
3
+ module Revisor
4
+
5
+ @@server_executable = "revisor"
6
+ @@server_pid = nil
7
+
8
+ class << self
9
+ def server_executable
10
+ @@server_executable
11
+ end
12
+
13
+ def server_executable=(e)
14
+ @@server_executable = e
15
+ end
16
+
17
+ def server_pid
18
+ @@server_pid
19
+ end
20
+
21
+ def start_server(interface = "127.0.0.1", port = 8080, delay = 3)
22
+ if @@server_pid
23
+ raise "Revisor server already started"
24
+ else
25
+ @@server_pid = fork do
26
+ exec("#{@@server_executable} -l #{interface} -p #{port}")
27
+ end
28
+
29
+ at_exit do
30
+ if Revisor.server_pid && $$ != Revisor.server_pid
31
+ Revisor.stop_server
32
+ end
33
+ end
34
+
35
+ # TODO: more clever detection of server startup
36
+ sleep delay
37
+ @@server_pid
38
+ end
39
+ end
40
+
41
+ def stop_server
42
+ if @@server_pid
43
+ Process.kill("INT", @@server_pid)
44
+ @@server_pid = nil
45
+ else
46
+ raise "Revisor server is not started, so I couldn't stop it"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{revisor_client}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mikhail Lapshin"]
12
+ s.date = %q{2011-04-11}
13
+ s.description = %q{}
14
+ s.email = %q{sotakone@sotakone.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "README.md",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "lib/revisor.rb",
23
+ "lib/revisor/client.rb",
24
+ "lib/revisor/client/session.rb",
25
+ "lib/revisor/client/tab.rb",
26
+ "revisor_client.gemspec",
27
+ "test/session_tab_test.rb",
28
+ "test/session_test.rb",
29
+ "test/test_helper.rb",
30
+ "test/test_pages/send_key_event.html",
31
+ "test/test_pages/send_mouse_click_event.html",
32
+ "test/test_pages/send_mouse_move_event.html"
33
+ ]
34
+ s.homepage = %q{http://github.com/sotakone/revisor/}
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.6.2}
37
+ s.summary = %q{Ruby helper library for Revisor browser}
38
+ s.test_files = [
39
+ "test/session_tab_test.rb",
40
+ "test/session_test.rb",
41
+ "test/test_helper.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ else
49
+ end
50
+ else
51
+ end
52
+ end
53
+
@@ -0,0 +1,143 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class SessionTabTest < Test::Unit::TestCase
4
+ include RevisorTestHelper
5
+
6
+ def setup
7
+ create_client
8
+ @session = @client.start_session(:session_tab_test)
9
+ @tab = @session.create_tab :foo
10
+ end
11
+
12
+ def teardown
13
+ @session.stop
14
+ end
15
+
16
+ def test_session_tab_creation_and_deletion
17
+ assert_not_nil @tab
18
+ assert_equal :foo, @tab.name
19
+
20
+ tab2 = @session.create_tab :bar
21
+ assert tab2.destroy
22
+ end
23
+
24
+ def test_session_tab_creation_with_duplicate_name
25
+ assert_raise Revisor::Client::ServerError do
26
+ @session.create_tab :foo
27
+ end
28
+ end
29
+
30
+ def test_visit_url_and_wait_for_load
31
+ @tab.visit("http://example.com/")
32
+ @tab.wait_for_load
33
+ assert_equal "http://example.com/", @tab.evaluate_javascript("window.location.href")
34
+ end
35
+
36
+ def test_evaluate_javascript
37
+ @tab.visit("http://example.com/")
38
+ @tab.wait_for_load
39
+ assert_equal 4, @tab.evaluate_javascript("2 + 2")
40
+ end
41
+
42
+ def test_prompt_answer
43
+ @tab.visit("http://example.com/")
44
+ @tab.wait_for_load
45
+ @tab.set_prompt_answer("my answer", false)
46
+ assert_equal "my answer", @tab.evaluate_javascript("prompt('prompt')")
47
+
48
+ @tab.set_prompt_answer("my answer", true)
49
+ assert_equal "", @tab.evaluate_javascript("prompt('prompt')")
50
+ end
51
+
52
+ def test_confirm_answer
53
+ @tab.visit("http://example.com/")
54
+ @tab.wait_for_load
55
+ @tab.set_confirm_answer(true)
56
+ assert_equal true, @tab.evaluate_javascript("confirm('prompt')")
57
+
58
+ @tab.set_confirm_answer(false)
59
+ assert_equal false, @tab.evaluate_javascript("confirm('prompt')")
60
+ end
61
+
62
+ def test_save_screenshot
63
+ @tab.visit("http://example.com/")
64
+ @tab.wait_for_load
65
+
66
+ assert_equal "OK", @tab.save_screenshot("/tmp/twitter_screenshot.png", [1024, 1024])[:result]
67
+ assert File.exist?("/tmp/twitter_screenshot.png")
68
+ end
69
+
70
+ def test_wait_for_condition
71
+ @tab.visit("http://example.com/")
72
+ @tab.wait_for_load
73
+
74
+ # foo will be 42 after 1000 msec
75
+ @tab.evaluate_javascript("var foo = 0; setTimeout('foo = 42', 1000)")
76
+ assert @tab.wait_for_condition("foo == 42", 1000, 3)
77
+
78
+ # this one will never be true
79
+ assert_equal false, @tab.wait_for_condition("false", 1000, 3)
80
+ end
81
+
82
+ def test_send_mouse_click_event
83
+ @tab.visit(test_page_url("send_mouse_click_event"))
84
+ @tab.wait_for_load
85
+
86
+ @tab.e("window.scrollTo(40, 40)")
87
+
88
+ x, y = get_element_coordinates('first')
89
+ @tab.send_mouse_event "click", x + 10, y + 10
90
+
91
+ x, y = get_element_coordinates('second')
92
+ @tab.send_mouse_event "dblclick", x + 10, y + 10
93
+
94
+ # Third will be clicked only with Ctrl+Shift+Alt modifiers
95
+ x, y = get_element_coordinates('third')
96
+ @tab.send_mouse_event "click", x + 10, y + 10, :modifiers => ["control", "shift", "alt"]
97
+
98
+ assert_equal [true, true, true], @tab.e("[first_clicked, second_clicked, third_clicked]")
99
+ end
100
+
101
+ def test_send_mouse_move_event
102
+ @tab.visit(test_page_url("send_mouse_move_event"))
103
+ @tab.wait_for_load
104
+
105
+ x, y = get_element_coordinates('first')
106
+ @tab.send_mouse_event "move", x + 10, y + 10
107
+ @tab.send_mouse_event "move", x + 20, y + 20
108
+
109
+ assert_equal [[x + 10, y + 10], [x + 20, y + 20]], @tab.e("mousemove_coordinates")
110
+ end
111
+
112
+ def test_send_key_event
113
+ @tab.visit(test_page_url("send_key_event"))
114
+ @tab.wait_for_load
115
+
116
+ @tab.e("focusTextbox(1)")
117
+ @tab.send_key_event "press", "A", "A"
118
+ @tab.send_key_event "press", "B", "B"
119
+ @tab.send_key_event "press", "C", "C"
120
+ @tab.send_key_event "press", "Semicolon", "Ж"
121
+ @tab.send_key_event "press", "E", "У"
122
+ @tab.send_key_event "press", "Q", "Й"
123
+
124
+ assert_equal "ABCЖУЙ", @tab.e("document.getElementById('textbox1').value")
125
+
126
+ @tab.e("focusTextbox(2)")
127
+ @tab.send_key_event "down", "A", "A", :modifiers => ['shift']
128
+
129
+ @tab.e("focusTextbox(3)")
130
+ @tab.send_key_event "up", "A", "A", :modifiers => ['ctrl']
131
+
132
+ assert_equal [true, true], @tab.e("[keyWithShiftDowned, keyWithCtrlUpped]")
133
+ end
134
+
135
+ private
136
+
137
+ def get_element_coordinates(element_id)
138
+ x, y = @tab.e("[document.getElementById('#{element_id}').offsetLeft, document.getElementById('#{element_id}').offsetTop]")
139
+ assert x.is_a?(Fixnum) && y.is_a?(Fixnum)
140
+ return [x, y]
141
+ end
142
+
143
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class SessionTest < Test::Unit::TestCase
4
+ include RevisorTestHelper
5
+
6
+ def setup
7
+ create_client
8
+ end
9
+
10
+ def test_session_start_and_stop
11
+ session = @client.start_session(:default)
12
+ assert_equal :default, session.name
13
+ session.stop
14
+ end
15
+
16
+ def test_creation_of_session_with_duplicate_names
17
+ session = @client.start_session(:default)
18
+
19
+ assert_raise Revisor::Client::ServerError do
20
+ @client.start_session(:default)
21
+ end
22
+
23
+ @client.stop_session(:default)
24
+ end
25
+
26
+ def test_setting_and_getting_cookies
27
+ session = @client.start_session(:cookies_test)
28
+ session.set_cookies([{ :name => "foo", :value => "bar", :expires_at => Time.now + 3600 }], "http://sotakone.github.com")
29
+
30
+ c = session.get_cookies('http://sotakone.github.com/').first
31
+ assert_equal "foo", c['name']
32
+ assert_equal "bar", c['value']
33
+
34
+ # Also test cookie presence in JS
35
+ tab = session.create_tab "cookies_test"
36
+ tab.visit("http://sotakone.github.com/")
37
+ tab.wait_for_load
38
+
39
+ assert tab.e("document.cookie.match(/foo=bar/).length == 1")
40
+ tab.destroy
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(__FILE__), "/../lib/revisor")
2
+ require 'test/unit'
3
+
4
+ module SetupAndTeardownOnce
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def suite
11
+ my_suite = super
12
+ klass = self.class
13
+
14
+ def my_suite.run(*args)
15
+ klass = eval(@name)
16
+ klass.setup_once
17
+ super(*args)
18
+ klass.teardown_once
19
+ end
20
+
21
+ return my_suite
22
+ end
23
+
24
+ def setup_once
25
+ # Override me in child classes
26
+ end
27
+
28
+ def teardown_once
29
+ # Override me in child classes
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ module RevisorTestHelper
36
+ def self.included(base)
37
+ base.send(:include, SetupAndTeardownOnce)
38
+ base.send(:include, InstanceMethods)
39
+ base.send(:extend, ClassMethods)
40
+ end
41
+
42
+ module InstanceMethods
43
+ def create_client
44
+ @client = Revisor::Client.new("localhost", 8080)
45
+ end
46
+
47
+ def test_page_url(test_page_name)
48
+ "file://" + File.dirname(File.expand_path(__FILE__)) + "/test_pages/#{test_page_name}.html"
49
+ end
50
+ end
51
+
52
+ module ClassMethods
53
+ def setup_once
54
+ unless Revisor.server_pid
55
+ Revisor.server_executable = ENV['REVISOR_PATH'] || 'revisor'
56
+ Revisor.start_server
57
+ end
58
+ end
59
+
60
+ def teardown_once
61
+ Revisor.stop_server
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <title>send_key_event test page</title>
5
+
6
+ <script type="text/javascript">
7
+ var keyWithShiftDowned = false;
8
+ var keyWithCtrlUpped = false;
9
+
10
+ function focusTextbox(i) {
11
+ var tb = document.getElementById("textbox" + i);
12
+ tb.focus();
13
+ }
14
+
15
+ window.onload = function() {
16
+ document.getElementById("textbox2").onkeydown = function(e) {
17
+ if(e.shiftKey) {
18
+ keyWithShiftDowned = true;
19
+ }
20
+ };
21
+
22
+ document.getElementById("textbox3").onkeyup = function(e) {
23
+ if(e.ctrlKey) {
24
+ keyWithCtrlUpped = true;
25
+ }
26
+ };
27
+ }
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <input type="text" id="textbox1" />
32
+ <input type="text" id="textbox2" />
33
+ <input type="text" id="textbox3" />
34
+ </body>
35
+ </html>
@@ -0,0 +1,40 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <title>send_mouse_event test page</title>
5
+
6
+ <style type="text/css" media="screen">
7
+ body {
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+
12
+ #first, #second, #third {
13
+ width: 50px;
14
+ height: 50px;
15
+ position: absolute;
16
+ overflow: hidden;
17
+ }
18
+
19
+ #first {
20
+ background-color: #c00;
21
+ }
22
+
23
+ #second {
24
+ background-color: #0c0;
25
+ left: 2000px;
26
+ }
27
+
28
+ #third {
29
+ background-color: #00c;
30
+ left: 2000px;
31
+ top: 2000px;
32
+ }
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <div id="first" onclick="first_clicked = true; console.log('first clicked');">First</div>
37
+ <div id="second" ondblclick="second_clicked = true; console.log('second dblclicked');">Second</div>
38
+ <div id="third" onclick="third_clicked = (window.event.shiftKey && window.event.ctrlKey && window.event.altKey); console.log('third clicked');">Third</div>
39
+ </body>
40
+ </html>
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <title>send_mouse_move_event test page</title>
5
+
6
+ <style type="text/css" media="screen">
7
+ body {
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+
12
+ #first, #second, #third {
13
+ width: 50px;
14
+ height: 50px;
15
+ position: absolute;
16
+ overflow: hidden;
17
+ }
18
+
19
+ #first {
20
+ background-color: #c00;
21
+ left: 1000px;
22
+ top: 1000px;
23
+ }
24
+ </style>
25
+
26
+ <script type="text/javascript">
27
+ var mousemove_coordinates = [];
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <div id="first" onmousemove="mousemove_coordinates.push([window.event.pageX, window.event.pageY]);">First</div>
32
+ </body>
33
+ </html>
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: revisor_client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Mikhail Lapshin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-11 00:00:00 +04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: ""
23
+ email: sotakone@sotakone.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.md
30
+ files:
31
+ - README.md
32
+ - Rakefile
33
+ - VERSION
34
+ - lib/revisor.rb
35
+ - lib/revisor/client.rb
36
+ - lib/revisor/client/session.rb
37
+ - lib/revisor/client/tab.rb
38
+ - revisor_client.gemspec
39
+ - test/session_tab_test.rb
40
+ - test/session_test.rb
41
+ - test/test_helper.rb
42
+ - test/test_pages/send_key_event.html
43
+ - test/test_pages/send_mouse_click_event.html
44
+ - test/test_pages/send_mouse_move_event.html
45
+ has_rdoc: true
46
+ homepage: http://github.com/sotakone/revisor/
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.6.2
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby helper library for Revisor browser
79
+ test_files:
80
+ - test/session_tab_test.rb
81
+ - test/session_test.rb
82
+ - test/test_helper.rb