terminus 0.1.0 → 0.2.0
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.rdoc +71 -20
- data/bin/terminus +21 -9
- data/lib/capybara/driver/terminus.rb +47 -0
- data/lib/public/loader.js +1 -0
- data/lib/public/style.css +7 -2
- data/lib/public/syn.js +2355 -0
- data/lib/public/terminus.js +294 -1
- data/lib/terminus.rb +50 -6
- data/lib/terminus/application.rb +9 -4
- data/lib/terminus/browser.rb +140 -0
- data/lib/terminus/controller.rb +78 -0
- data/lib/terminus/node.rb +68 -0
- data/lib/terminus/server.rb +1 -6
- data/lib/terminus/timeouts.rb +27 -0
- data/lib/views/bookmarklet.erb +14 -4
- data/lib/views/index.erb +11 -8
- data/spec/spec_helper.rb +22 -0
- data/spec/terminus_driver_spec.rb +20 -0
- data/spec/terminus_session_spec.rb +32 -0
- metadata +105 -42
- data/lib/public/js.packages.js +0 -1
@@ -0,0 +1,78 @@
|
|
1
|
+
module Terminus
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
include Timeouts
|
5
|
+
attr_accessor :dock_host
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@connected = false
|
9
|
+
@browsers = {}
|
10
|
+
@ping_callbacks = []
|
11
|
+
trap('INT') { exit }
|
12
|
+
end
|
13
|
+
|
14
|
+
def browser(id = nil)
|
15
|
+
return @browser if id.nil?
|
16
|
+
@browsers[id] ||= Browser.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def browser=(params)
|
20
|
+
@browser = case params
|
21
|
+
when :docked then @browsers.values.find { |b| b.docked? }
|
22
|
+
when Browser then params
|
23
|
+
when Hash then
|
24
|
+
wait_with_timeout(:named_window) do
|
25
|
+
@browsers.values.find { |b| b.name == params[:name] }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop_browser(browser)
|
31
|
+
@browsers.delete(browser.id)
|
32
|
+
@browser = nil if @browser == browser
|
33
|
+
end
|
34
|
+
|
35
|
+
def ensure_browser
|
36
|
+
ensure_connection
|
37
|
+
wait_with_timeout(:browser) { @browser }
|
38
|
+
end
|
39
|
+
|
40
|
+
def ensure_docked_browser
|
41
|
+
ensure_connection
|
42
|
+
wait_with_timeout(:docked_browser) { @browsers.any? { |id,b| b.docked? } }
|
43
|
+
end
|
44
|
+
|
45
|
+
def messenger
|
46
|
+
Terminus.ensure_reactor_running
|
47
|
+
return @messenger if defined?(@messenger)
|
48
|
+
|
49
|
+
@messenger = Faye::Client.new(Terminus.endpoint)
|
50
|
+
|
51
|
+
@messenger.subscribe('/terminus/ping', &method(:accept_ping))
|
52
|
+
@messenger.subscribe('/terminus/results', &method(:accept_result))
|
53
|
+
@messenger
|
54
|
+
end
|
55
|
+
|
56
|
+
def return_to_dock
|
57
|
+
@browsers.each { |id, b| b.return_to_dock }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def accept_ping(message)
|
63
|
+
browser(message['id']).ping!(message)
|
64
|
+
end
|
65
|
+
|
66
|
+
def accept_result(message)
|
67
|
+
browser(message['id']).result!(message)
|
68
|
+
end
|
69
|
+
|
70
|
+
def ensure_connection
|
71
|
+
return if @connected
|
72
|
+
messenger.connect { @connected = true }
|
73
|
+
wait_with_timeout(:connection) { @connected }
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Terminus
|
2
|
+
class Node
|
3
|
+
|
4
|
+
attr_reader :id
|
5
|
+
|
6
|
+
def initialize(browser, id)
|
7
|
+
@browser, @id = browser, id
|
8
|
+
end
|
9
|
+
|
10
|
+
def click
|
11
|
+
page = @browser.page_id
|
12
|
+
command = @browser.tell([:click, @id])
|
13
|
+
|
14
|
+
@browser.wait_with_timeout(:click_response) do
|
15
|
+
@browser.result(command) || (@browser.page_id != page)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def drag_to(node)
|
20
|
+
@browser.ask([:drag, {:from => @id, :to => node.id}])
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(xpath)
|
24
|
+
@browser.ask([:find, xpath, @id]).map { |id| Node.new(@browser, id) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def select
|
28
|
+
@browser.ask([:select, @id])
|
29
|
+
end
|
30
|
+
|
31
|
+
def set(value)
|
32
|
+
result = @browser.ask([:set, @id, value])
|
33
|
+
raise Capybara::NotSupportedByDriverError.new if result == 'not_allowed'
|
34
|
+
end
|
35
|
+
|
36
|
+
def trigger(event_type)
|
37
|
+
@browser.ask([:trigger, @id, event_type])
|
38
|
+
end
|
39
|
+
|
40
|
+
def unselect
|
41
|
+
allowed = @browser.ask([:unselect, @id])
|
42
|
+
raise Capybara::UnselectNotAllowed.new unless allowed
|
43
|
+
end
|
44
|
+
|
45
|
+
alias :select_option :select
|
46
|
+
alias :unselect_option :unselect
|
47
|
+
|
48
|
+
SYNC_DSL_METHODS = [ [:[], :attribute],
|
49
|
+
:tag_name,
|
50
|
+
:text,
|
51
|
+
:value,
|
52
|
+
[:visible?, :is_visible]
|
53
|
+
]
|
54
|
+
|
55
|
+
SYNC_DSL_METHODS.each do |method|
|
56
|
+
if Array === method
|
57
|
+
name, command = *method
|
58
|
+
else
|
59
|
+
name = command = method
|
60
|
+
end
|
61
|
+
define_method(name) do |*args|
|
62
|
+
@browser.ask([command, @id, *args])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/lib/terminus/server.rb
CHANGED
@@ -16,7 +16,7 @@ module Terminus
|
|
16
16
|
def run!
|
17
17
|
return if running?
|
18
18
|
handler = Rack::Handler.get('thin')
|
19
|
-
ensure_reactor_running
|
19
|
+
Terminus.ensure_reactor_running
|
20
20
|
handler.run(app, :Port => @options[:port]) { |s| @server = s }
|
21
21
|
end
|
22
22
|
|
@@ -38,11 +38,6 @@ module Terminus
|
|
38
38
|
app.get_client
|
39
39
|
end
|
40
40
|
|
41
|
-
def ensure_reactor_running!
|
42
|
-
Thread.new { EM.run unless EM.reactor_running? }
|
43
|
-
while not EM.reactor_running?; end
|
44
|
-
end
|
45
|
-
|
46
41
|
end
|
47
42
|
end
|
48
43
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Terminus
|
2
|
+
module Timeouts
|
3
|
+
|
4
|
+
class TimeoutError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
include Faye::Timeouts
|
8
|
+
TIMEOUT = 10
|
9
|
+
|
10
|
+
def wait_with_timeout(name, duration = TIMEOUT, &predicate)
|
11
|
+
result, time_out = nil, false
|
12
|
+
add_timeout(name, duration) { time_out = true }
|
13
|
+
|
14
|
+
while !result and !time_out
|
15
|
+
result = predicate.call
|
16
|
+
sleep 0.1
|
17
|
+
end
|
18
|
+
|
19
|
+
raise TimeoutError.new("Waited #{duration}s but could not get a #{name}") if time_out
|
20
|
+
remove_timeout(name)
|
21
|
+
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/lib/views/bookmarklet.erb
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
var faye = '<%= ::Terminus::FAYE_MOUNT %>',
|
3
3
|
host = '<%= host %>';
|
4
4
|
|
5
|
+
var html = document.getElementsByTagName('html')[0],
|
6
|
+
source = '<html>\n' + html.innerHTML + '\n</html>\n';
|
7
|
+
|
8
|
+
JSCLASS_PATH = host + '/js.class/';
|
9
|
+
|
5
10
|
var withPackageManager = function(callback) {
|
6
11
|
if (window.JS && JS.Packages) return callback();
|
7
12
|
|
@@ -9,7 +14,7 @@
|
|
9
14
|
head = document.getElementsByTagName('head')[0];
|
10
15
|
|
11
16
|
script.type = 'text/javascript';
|
12
|
-
script.src = host + '/
|
17
|
+
script.src = host + '/loader.js';
|
13
18
|
|
14
19
|
script.onload = script.onreadystatechange = function() {
|
15
20
|
var state = script.readyState;
|
@@ -25,14 +30,19 @@
|
|
25
30
|
withPackageManager(function() {
|
26
31
|
JS.Packages(function() {
|
27
32
|
this.file(host + faye + '/client.js')
|
28
|
-
.provides('Faye', 'Faye.Client')
|
33
|
+
.provides('Faye', 'Faye.Client')
|
34
|
+
.setup(function() { Faye.Client.prototype.MAX_DELAY = 0 });
|
35
|
+
|
36
|
+
this.file(host + '/syn.js')
|
37
|
+
.provides('Syn');
|
29
38
|
|
30
39
|
this.file(host + '/terminus.js')
|
31
|
-
.requires('Faye.Client')
|
40
|
+
.requires('Faye.Client', 'document.evaluate', 'Syn')
|
32
41
|
.provides('Terminus');
|
33
42
|
});
|
34
43
|
|
35
|
-
require('Terminus', function() {
|
44
|
+
JS.require('Terminus', function() {
|
45
|
+
Terminus.originalSource = source;
|
36
46
|
Terminus.connect(host + faye);
|
37
47
|
});
|
38
48
|
});
|
data/lib/views/index.erb
CHANGED
@@ -7,21 +7,24 @@
|
|
7
7
|
<title>Terminus</title>
|
8
8
|
</head>
|
9
9
|
<body>
|
10
|
+
<%= Terminus.driver_script request.host %>
|
10
11
|
|
11
12
|
<div class="container">
|
12
13
|
<h1><a href="javascript:<%= bookmarklet %>">Terminus</a>.</h1>
|
13
14
|
|
14
|
-
<p>
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
<p>This is the holding page. Browsers visiting this page are
|
16
|
+
connected to your Terminus server and can be taken over and
|
17
|
+
controlled by your Ruby programs.</p>
|
18
|
+
|
19
|
+
<p>That big pink title up there is a bookmarklet. Drag it up
|
20
|
+
to your bookmarks bar and use it to connect any page on the
|
21
|
+
web to your Terminus console.</p>
|
19
22
|
</div>
|
20
23
|
|
21
24
|
<div class="footer">
|
22
|
-
<p>Copyright © 2010
|
23
|
-
|
24
|
-
|
25
|
+
<p>Copyright © 2010 <a href="http://jcoglan.com">James Coglan</a>.<br>
|
26
|
+
Released under the MIT license.<br>
|
27
|
+
Source code hosted by <a href="http://github.com/jcoglan/terminus">GitHub</a>.</p>
|
25
28
|
</div>
|
26
29
|
|
27
30
|
</body>
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
root = File.dirname(__FILE__) + '/../'
|
2
|
+
|
3
|
+
require root + 'vendor/capybara/spec/spec_helper'
|
4
|
+
require root + 'lib/terminus'
|
5
|
+
|
6
|
+
class TestApp
|
7
|
+
template :layout do
|
8
|
+
<<-HTML
|
9
|
+
<html>
|
10
|
+
<head>
|
11
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
12
|
+
<title>Capybara Driver Test</title>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<%= yield %>
|
16
|
+
<%= Terminus.driver_script request.host %>
|
17
|
+
</body>
|
18
|
+
</html>
|
19
|
+
HTML
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Capybara::Driver::Terminus do
|
4
|
+
before do
|
5
|
+
@driver = Capybara::Driver::Terminus.new(TestApp)
|
6
|
+
Terminus.ensure_docked_browser
|
7
|
+
Terminus.browser = :docked
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Terminus.return_to_dock
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like "driver"
|
15
|
+
it_should_behave_like "driver with javascript support"
|
16
|
+
it_should_behave_like "driver without status code support"
|
17
|
+
it_should_behave_like "driver with support for window switching"
|
18
|
+
it_should_behave_like "driver with cookies support"
|
19
|
+
it_should_behave_like "driver with infinite redirect detection"
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Capybara::Session do
|
4
|
+
context 'with terminus driver' do
|
5
|
+
before do
|
6
|
+
@session = Capybara::Session.new(:terminus, TestApp)
|
7
|
+
Terminus.ensure_docked_browser
|
8
|
+
Terminus.browser = :docked
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
Terminus.return_to_dock
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#driver' do
|
16
|
+
it "should be a terminus driver" do
|
17
|
+
@session.driver.should be_an_instance_of(Capybara::Driver::Terminus)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#mode' do
|
22
|
+
it "should remember the mode" do
|
23
|
+
@session.mode.should == :terminus
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it_should_behave_like "session"
|
28
|
+
it_should_behave_like "session without headers support"
|
29
|
+
it_should_behave_like "session with javascript support"
|
30
|
+
it_should_behave_like "session without status code support"
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terminus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- James Coglan
|
@@ -9,81 +14,125 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-10-24 00:00:00 +01:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rack
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 0
|
23
31
|
version: "1.0"
|
24
|
-
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
35
|
name: thin
|
27
|
-
|
28
|
-
|
29
|
-
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
30
39
|
requirements:
|
31
40
|
- - ">="
|
32
41
|
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 1
|
44
|
+
- 2
|
33
45
|
version: "1.2"
|
34
|
-
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
35
48
|
- !ruby/object:Gem::Dependency
|
36
49
|
name: eventmachine
|
37
|
-
|
38
|
-
|
39
|
-
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
40
53
|
requirements:
|
41
54
|
- - ">="
|
42
55
|
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
- 12
|
43
59
|
version: "0.12"
|
44
|
-
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
45
62
|
- !ruby/object:Gem::Dependency
|
46
63
|
name: faye
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
- 5
|
73
|
+
- 2
|
74
|
+
version: 0.5.2
|
47
75
|
type: :runtime
|
48
|
-
|
49
|
-
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: capybara
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
50
82
|
requirements:
|
51
|
-
- -
|
83
|
+
- - ">="
|
52
84
|
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
- 4
|
88
|
+
- 0
|
89
|
+
version: 0.4.0
|
90
|
+
type: :runtime
|
91
|
+
version_requirements: *id005
|
55
92
|
- !ruby/object:Gem::Dependency
|
56
93
|
name: sinatra
|
57
|
-
|
58
|
-
|
59
|
-
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
60
97
|
requirements:
|
61
98
|
- - ">="
|
62
99
|
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 1
|
102
|
+
- 0
|
63
103
|
version: "1.0"
|
64
|
-
|
104
|
+
type: :runtime
|
105
|
+
version_requirements: *id006
|
65
106
|
- !ruby/object:Gem::Dependency
|
66
107
|
name: packr
|
67
|
-
|
68
|
-
|
69
|
-
|
108
|
+
prerelease: false
|
109
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
70
111
|
requirements:
|
71
112
|
- - ">="
|
72
113
|
- !ruby/object:Gem::Version
|
114
|
+
segments:
|
115
|
+
- 3
|
116
|
+
- 1
|
73
117
|
version: "3.1"
|
74
|
-
|
118
|
+
type: :runtime
|
119
|
+
version_requirements: *id007
|
75
120
|
- !ruby/object:Gem::Dependency
|
76
121
|
name: oyster
|
77
|
-
|
78
|
-
|
79
|
-
|
122
|
+
prerelease: false
|
123
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
80
125
|
requirements:
|
81
126
|
- - ">="
|
82
127
|
- !ruby/object:Gem::Version
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
- 9
|
83
131
|
version: "0.9"
|
84
|
-
|
132
|
+
type: :runtime
|
133
|
+
version_requirements: *id008
|
85
134
|
description:
|
86
|
-
email: jcoglan@
|
135
|
+
email: jcoglan@gmail.com
|
87
136
|
executables:
|
88
137
|
- terminus
|
89
138
|
extensions: []
|
@@ -92,14 +141,24 @@ extra_rdoc_files:
|
|
92
141
|
- README.rdoc
|
93
142
|
files:
|
94
143
|
- README.rdoc
|
95
|
-
- lib/terminus.rb
|
96
|
-
- lib/public/style.css
|
97
|
-
- lib/public/terminus.js
|
98
|
-
- lib/public/js.packages.js
|
99
|
-
- lib/views/bookmarklet.erb
|
100
144
|
- lib/views/index.erb
|
101
|
-
- lib/
|
145
|
+
- lib/views/bookmarklet.erb
|
146
|
+
- lib/public/loader.js
|
147
|
+
- lib/public/syn.js
|
148
|
+
- lib/public/terminus.js
|
149
|
+
- lib/public/style.css
|
150
|
+
- lib/terminus.rb
|
151
|
+
- lib/terminus/node.rb
|
152
|
+
- lib/terminus/timeouts.rb
|
153
|
+
- lib/terminus/browser.rb
|
102
154
|
- lib/terminus/application.rb
|
155
|
+
- lib/terminus/server.rb
|
156
|
+
- lib/terminus/controller.rb
|
157
|
+
- lib/capybara/driver/terminus.rb
|
158
|
+
- spec/terminus_session_spec.rb
|
159
|
+
- spec/terminus_driver_spec.rb
|
160
|
+
- spec/spec_helper.rb
|
161
|
+
- bin/terminus
|
103
162
|
has_rdoc: true
|
104
163
|
homepage: http://github.com/jcoglan/terminus
|
105
164
|
licenses: []
|
@@ -111,23 +170,27 @@ rdoc_options:
|
|
111
170
|
require_paths:
|
112
171
|
- lib
|
113
172
|
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
114
174
|
requirements:
|
115
175
|
- - ">="
|
116
176
|
- !ruby/object:Gem::Version
|
177
|
+
segments:
|
178
|
+
- 0
|
117
179
|
version: "0"
|
118
|
-
version:
|
119
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
none: false
|
120
182
|
requirements:
|
121
183
|
- - ">="
|
122
184
|
- !ruby/object:Gem::Version
|
185
|
+
segments:
|
186
|
+
- 0
|
123
187
|
version: "0"
|
124
|
-
version:
|
125
188
|
requirements: []
|
126
189
|
|
127
190
|
rubyforge_project:
|
128
|
-
rubygems_version: 1.3.
|
191
|
+
rubygems_version: 1.3.7
|
129
192
|
signing_key:
|
130
193
|
specification_version: 3
|
131
|
-
summary:
|
194
|
+
summary: Capybara driver for cross-browser testing
|
132
195
|
test_files: []
|
133
196
|
|