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 +24 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/lib/revisor/client/session.rb +53 -0
- data/lib/revisor/client/tab.rb +93 -0
- data/lib/revisor/client.rb +66 -0
- data/lib/revisor.rb +50 -0
- data/revisor_client.gemspec +53 -0
- data/test/session_tab_test.rb +143 -0
- data/test/session_test.rb +42 -0
- data/test/test_helper.rb +64 -0
- data/test/test_pages/send_key_event.html +35 -0
- data/test/test_pages/send_mouse_click_event.html +40 -0
- data/test/test_pages/send_mouse_move_event.html +33 -0
- metadata +82 -0
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
|
data/test/test_helper.rb
ADDED
@@ -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
|