terminus 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -89
- data/bin/terminus +7 -23
- data/lib/capybara/driver/terminus.rb +30 -11
- data/lib/terminus.rb +33 -22
- data/lib/terminus/application.rb +13 -7
- data/lib/terminus/browser.rb +110 -28
- data/lib/terminus/controller.rb +51 -20
- data/lib/terminus/host.rb +22 -0
- data/lib/terminus/node.rb +23 -4
- data/lib/terminus/proxy.rb +62 -0
- data/lib/terminus/proxy/driver_body.rb +63 -0
- data/lib/terminus/proxy/external.rb +25 -0
- data/lib/terminus/proxy/rewrite.rb +20 -0
- data/lib/terminus/public/icon.png +0 -0
- data/lib/terminus/public/loader.js +1 -0
- data/lib/terminus/public/style.css +43 -0
- data/lib/terminus/public/syn/browsers.js +150 -0
- data/lib/terminus/public/syn/drag/drag.js +322 -0
- data/lib/terminus/public/syn/key.js +905 -0
- data/lib/terminus/public/syn/mouse.js +284 -0
- data/lib/terminus/public/syn/synthetic.js +830 -0
- data/lib/{public → terminus/public}/terminus.js +109 -40
- data/lib/terminus/server.rb +5 -12
- data/lib/terminus/timeouts.rb +2 -2
- data/lib/{views/bookmarklet.erb → terminus/views/bootstrap.erb} +17 -12
- data/lib/terminus/views/index.erb +21 -0
- data/lib/terminus/views/infinite.html +16 -0
- data/spec/reports/chrome.txt +748 -0
- data/spec/reports/firefox.txt +748 -0
- data/spec/reports/opera.txt +748 -0
- data/spec/reports/safari.txt +748 -0
- data/spec/spec_helper.rb +18 -14
- data/spec/terminus_driver_spec.rb +7 -5
- data/spec/terminus_session_spec.rb +5 -18
- metadata +71 -57
- data/lib/public/loader.js +0 -1
- data/lib/public/style.css +0 -49
- data/lib/public/syn.js +0 -2355
- data/lib/views/index.erb +0 -32
data/README.rdoc
CHANGED
@@ -1,112 +1,50 @@
|
|
1
1
|
= Terminus
|
2
2
|
|
3
|
-
* http://
|
3
|
+
* http://terminus.jcoglan.com
|
4
4
|
|
5
5
|
Terminus is an experimental Capybara driver implemented in client-side
|
6
|
-
JavaScript. It lets you script your application in any browser on any
|
7
|
-
|
8
|
-
|
6
|
+
JavaScript. It lets you script your application in any browser on any device,
|
7
|
+
without needing browser plugins. This allows several types of testing to be
|
8
|
+
automated:
|
9
9
|
|
10
10
|
* Cross-browser testing
|
11
11
|
* Multi-browser interaction e.g. messaging apps
|
12
|
-
*
|
13
|
-
|
14
|
-
It is also a remote scripting tool, giving you a REPL that lets you
|
15
|
-
run JavaScript in any number of browsers at once.
|
16
|
-
|
17
|
-
|
18
|
-
== Usage
|
19
|
-
|
20
|
-
Terminus is a Capybara driver. For the most part, you will not use
|
21
|
-
it directly: you will use the Capybara API and it will send instructions
|
22
|
-
to Terminus for execution. To set Terminus as your driver:
|
23
|
-
|
24
|
-
require 'capybara'
|
25
|
-
require 'terminus'
|
26
|
-
|
27
|
-
Capybara.current_driver = :terminus
|
28
|
-
|
29
|
-
Terminus does require some extra setup before you can use it to control
|
30
|
-
your app. First up, you need to start the Terminus server on the machine
|
31
|
-
where your application will be running:
|
32
|
-
|
33
|
-
$ terminus
|
34
|
-
|
35
|
-
This starts the server on port 7004. Now open a browser at
|
36
|
-
http://127.0.0.1:7004/. (I recommend using the IP address of the Terminus
|
37
|
-
host; Chrome has bugs that can stop WebSockets working if you use the
|
38
|
-
hostname.) This is the 'holding page'. A browser is said to be 'docked'
|
39
|
-
while it is visiting this page, meaning it is ready and waiting to run
|
40
|
-
some tests for you.
|
41
|
-
|
42
|
-
To let Terminus control your app's pages, you need to include this
|
43
|
-
just before the closing +body+ tag when in testing mode:
|
44
|
-
|
45
|
-
<!-- For example if you're using Rails -->
|
46
|
-
|
47
|
-
<% if Rails.env.test? %>
|
48
|
-
<%= Terminus.driver_script '127.0.0.1' %>
|
49
|
-
<% end %>
|
50
|
-
|
51
|
-
If the browser you're using is on a different machine to the Terminus
|
52
|
-
server, replace <tt>127.0.0.1</tt> with the Terminus machine's IP as
|
53
|
-
seen by the browser. For example if I'm running the browser in VirtualBox
|
54
|
-
and the Terminus server on the host OS, then I set the IP to <tt>10.0.2.2</tt>.
|
55
|
-
You could also use <tt>request.host</tt> to get the right IP automatically
|
56
|
-
if you're running your app and your Terminus server on the same machine.
|
57
|
-
|
58
|
-
Finally, in your tests you need to make sure there's a docked browser
|
59
|
-
and select it. In a 'before' block, run the following:
|
60
|
-
|
61
|
-
Terminus.ensure_docked_browser
|
62
|
-
Terminus.browser = :docked
|
63
|
-
|
64
|
-
After each test is finished, you need to return the browser to the
|
65
|
-
holding page to make it ready to accept new work. In an 'after' block:
|
66
|
-
|
67
|
-
Terminus.return_to_dock
|
68
|
-
|
69
|
-
This returns all currently connected browsers to the holding page.
|
12
|
+
* Testing on remote machines, phones, iPads etc.
|
70
13
|
|
71
14
|
|
72
15
|
== Notes / to-do
|
73
16
|
|
74
|
-
* Support IE, which has no built-in XPath engine for querying the DOM.
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
* Allow <tt>Terminus.browser=</tt> to select browsers by name and
|
79
|
-
version so we can control multiple browsers at once.
|
17
|
+
* Support IE, which has no built-in XPath engine for querying the DOM. I'm
|
18
|
+
working on Pathology (see http://github.com/jcoglan/pathology) to try and fix
|
19
|
+
this but it's currently not fast enough.
|
80
20
|
|
81
|
-
* It's slow, especially at filling out forms. Use it to sanity-check
|
82
|
-
|
21
|
+
* It's slow, especially at filling out forms. Use it to sanity-check your
|
22
|
+
cross-browser code and JavaScript, not to test your whole app.
|
83
23
|
|
84
|
-
* It can be a little brittle, and occasionally there seem to be race
|
85
|
-
|
24
|
+
* It can be a little brittle, and occasionally there seem to be race conditions
|
25
|
+
when running the Capybara specs.
|
86
26
|
|
87
27
|
|
88
28
|
== License
|
89
29
|
|
90
30
|
(The MIT License)
|
91
31
|
|
92
|
-
Copyright (c) 2010 James Coglan
|
32
|
+
Copyright (c) 2010-2011 James Coglan
|
93
33
|
|
94
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
the following conditions:
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
35
|
+
this software and associated documentation files (the 'Software'), to deal in
|
36
|
+
the Software without restriction, including without limitation the rights to use,
|
37
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
38
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
39
|
+
subject to the following conditions:
|
101
40
|
|
102
|
-
The above copyright notice and this permission notice shall be
|
103
|
-
|
41
|
+
The above copyright notice and this permission notice shall be included in all
|
42
|
+
copies or substantial portions of the Software.
|
104
43
|
|
105
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
44
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
45
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
46
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
47
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
48
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
49
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
112
50
|
|
data/bin/terminus
CHANGED
@@ -5,7 +5,7 @@ require 'oyster'
|
|
5
5
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/terminus')
|
6
6
|
|
7
7
|
spec = Oyster.spec do
|
8
|
-
name "terminus --
|
8
|
+
name "terminus -- Control web browsers with Ruby"
|
9
9
|
synopsis "terminus [--port PORT]"
|
10
10
|
|
11
11
|
integer :port, :default => Terminus::DEFAULT_PORT
|
@@ -15,30 +15,14 @@ begin
|
|
15
15
|
options = spec.parse
|
16
16
|
app = Terminus.create(options)
|
17
17
|
|
18
|
-
app.run!
|
19
|
-
puts "Terminus server running on port #{options[:port]}"
|
20
|
-
puts "Press CTRL-C to exit"
|
21
|
-
puts ""
|
22
|
-
|
23
18
|
trap("INT") { app.stop! ; exit }
|
24
19
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
loop {
|
33
|
-
script, result = Readline.readline('>> '), nil
|
34
|
-
Readline::HISTORY.push(script)
|
35
|
-
app.execute(script) { |r| result = r }
|
36
|
-
}
|
37
|
-
rescue LoadError
|
38
|
-
puts "If you install the readline library, you'll get a console"
|
39
|
-
puts "that lets you execute JavaScript in connected browsers."
|
40
|
-
puts ""
|
41
|
-
end
|
20
|
+
Terminus.port = options[:port]
|
21
|
+
|
22
|
+
puts "Terminus server running on port #{Terminus.port}"
|
23
|
+
puts "Press CTRL-C to exit"
|
24
|
+
puts ""
|
25
|
+
app.run!
|
42
26
|
|
43
27
|
rescue Oyster::HelpRendered
|
44
28
|
end
|
@@ -1,30 +1,49 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
1
|
class Capybara::Driver::Terminus < Capybara::Driver::Base
|
4
|
-
|
2
|
+
attr_reader :options
|
3
|
+
|
4
|
+
def initialize(app = nil, options = {})
|
5
5
|
raise ArgumentError.new if app.nil?
|
6
|
-
|
6
|
+
|
7
|
+
@app = Terminus::Proxy[app]
|
8
|
+
@options = options
|
7
9
|
@rack_server = Capybara::Server.new(@app)
|
8
|
-
|
10
|
+
|
11
|
+
@rack_server.boot
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(xpath)
|
15
|
+
browser.find(xpath, self)
|
9
16
|
end
|
10
17
|
|
11
18
|
def visit(path)
|
12
|
-
browser.visit
|
19
|
+
browser.visit(@rack_server.url(path))
|
13
20
|
end
|
14
21
|
|
15
22
|
extend Forwardable
|
16
23
|
def_delegators :browser, :body,
|
17
|
-
:current_path,
|
18
24
|
:current_url,
|
19
25
|
:evaluate_script,
|
20
26
|
:execute_script,
|
21
|
-
:find,
|
22
27
|
:reset!,
|
23
|
-
:
|
28
|
+
:response_headers,
|
29
|
+
:source,
|
30
|
+
:status_code
|
24
31
|
|
25
32
|
def within_window(name)
|
26
33
|
current_browser = browser
|
27
|
-
Terminus.browser =
|
34
|
+
Terminus.browser = browser.id + '/' + name
|
35
|
+
yield
|
36
|
+
Terminus.browser = current_browser
|
37
|
+
end
|
38
|
+
|
39
|
+
def within_frame(name)
|
40
|
+
frame_src = browser.frame_src(name)
|
41
|
+
frame = browser.frames.find do |frame|
|
42
|
+
frame.current_url == frame_src or
|
43
|
+
frame.current_path == frame_src
|
44
|
+
end
|
45
|
+
current_browser = browser
|
46
|
+
Terminus.browser = frame
|
28
47
|
yield
|
29
48
|
Terminus.browser = current_browser
|
30
49
|
end
|
@@ -32,7 +51,7 @@ class Capybara::Driver::Terminus < Capybara::Driver::Base
|
|
32
51
|
private
|
33
52
|
|
34
53
|
def browser
|
35
|
-
Terminus.
|
54
|
+
Terminus.ensure_browsers
|
36
55
|
Terminus.browser
|
37
56
|
end
|
38
57
|
end
|
data/lib/terminus.rb
CHANGED
@@ -1,38 +1,39 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'uri'
|
3
|
-
require 'rubygems'
|
4
3
|
require 'rack'
|
5
4
|
require 'thin'
|
6
5
|
require 'eventmachine'
|
7
6
|
require 'faye'
|
8
|
-
require 'capybara'
|
9
7
|
require 'sinatra'
|
10
8
|
require 'packr'
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
%w[ application
|
15
|
-
server
|
16
|
-
timeouts
|
17
|
-
controller
|
18
|
-
browser
|
19
|
-
node
|
20
|
-
|
21
|
-
].each do |file|
|
22
|
-
require File.join(root, 'terminus', file)
|
23
|
-
end
|
24
|
-
|
25
|
-
require root + '/capybara/driver/terminus'
|
9
|
+
require 'capybara'
|
10
|
+
require 'rack-proxy'
|
11
|
+
require 'useragent'
|
26
12
|
|
27
13
|
Thin::Logging.silent = true
|
28
14
|
|
29
15
|
module Terminus
|
30
|
-
VERSION = '0.2.0'
|
31
16
|
FAYE_MOUNT = '/messaging'
|
32
17
|
DEFAULT_HOST = 'localhost'
|
33
18
|
DEFAULT_PORT = 7004
|
19
|
+
LOCALHOST = /^(localhost|0\.0\.0\.0|127\.0\.0\.1)$/
|
20
|
+
RETRY_LIMIT = 3
|
21
|
+
|
22
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
23
|
+
autoload :Application, ROOT + '/terminus/application'
|
24
|
+
autoload :Browser, ROOT + '/terminus/browser'
|
25
|
+
autoload :Controller, ROOT + '/terminus/controller'
|
26
|
+
autoload :Host, ROOT + '/terminus/host'
|
27
|
+
autoload :Node, ROOT + '/terminus/node'
|
28
|
+
autoload :Proxy, ROOT + '/terminus/proxy'
|
29
|
+
autoload :Server, ROOT + '/terminus/server'
|
30
|
+
autoload :Timeouts, ROOT + '/terminus/timeouts'
|
31
|
+
|
32
|
+
require ROOT + '/capybara/driver/terminus'
|
34
33
|
|
35
34
|
class << self
|
35
|
+
attr_accessor :debug
|
36
|
+
|
36
37
|
def create(options = {})
|
37
38
|
Server.new(options)
|
38
39
|
end
|
@@ -42,7 +43,7 @@ module Terminus
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def endpoint(host = DEFAULT_HOST)
|
45
|
-
"http://#{host}:#{
|
46
|
+
"http://#{host}:#{port}#{FAYE_MOUNT}"
|
46
47
|
end
|
47
48
|
|
48
49
|
def ensure_reactor_running
|
@@ -50,12 +51,22 @@ module Terminus
|
|
50
51
|
while not EM.reactor_running?; end
|
51
52
|
end
|
52
53
|
|
54
|
+
def port
|
55
|
+
@port || DEFAULT_PORT
|
56
|
+
end
|
57
|
+
|
58
|
+
def port=(port)
|
59
|
+
@port = port.to_i
|
60
|
+
end
|
61
|
+
|
53
62
|
extend Forwardable
|
54
63
|
def_delegators :controller, :browser,
|
64
|
+
:browsers,
|
55
65
|
:browser=,
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
66
|
+
:ensure_browsers,
|
67
|
+
:return_to_dock,
|
68
|
+
:rewrite_local,
|
69
|
+
:rewrite_remote
|
59
70
|
|
60
71
|
private
|
61
72
|
|
data/lib/terminus/application.rb
CHANGED
@@ -4,16 +4,16 @@ module Terminus
|
|
4
4
|
ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
|
5
5
|
|
6
6
|
set :static, true
|
7
|
-
set :public, ROOT + '/public'
|
8
|
-
set :views, ROOT + '/views'
|
7
|
+
set :public, ROOT + '/terminus/public'
|
8
|
+
set :views, ROOT + '/terminus/views'
|
9
9
|
|
10
10
|
def self.driver_script(host)
|
11
|
-
%Q{<script type="text/javascript" src="http://#{host}:#{
|
11
|
+
%Q{<script type="text/javascript" src="http://#{host}:#{Terminus.port}/bootstrap.js"></script>}
|
12
12
|
end
|
13
13
|
|
14
14
|
helpers do
|
15
|
-
def
|
16
|
-
Packr.pack(erb(:
|
15
|
+
def bootstrap
|
16
|
+
Packr.pack(erb(:bootstrap), :shrink_vars => true)
|
17
17
|
end
|
18
18
|
|
19
19
|
def host
|
@@ -21,8 +21,14 @@ module Terminus
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
get
|
25
|
-
|
24
|
+
get '/' do
|
25
|
+
erb :index
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/bootstrap.js' do
|
29
|
+
headers 'Content-Type' => 'text/javascript'
|
30
|
+
bootstrap
|
31
|
+
end
|
26
32
|
|
27
33
|
end
|
28
34
|
end
|
data/lib/terminus/browser.rb
CHANGED
@@ -3,19 +3,36 @@ module Terminus
|
|
3
3
|
|
4
4
|
include Timeouts
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
extend Forwardable
|
7
|
+
def_delegator :@user_agent, :browser, :name
|
8
|
+
def_delegators :@user_agent, :os, :version
|
8
9
|
|
9
10
|
def initialize(controller)
|
10
|
-
@controller
|
11
|
-
@attributes
|
12
|
-
@docked
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@results
|
11
|
+
@controller = controller
|
12
|
+
@attributes = {}
|
13
|
+
@docked = false
|
14
|
+
@frames = Set.new
|
15
|
+
@namespace = Faye::Namespace.new
|
16
|
+
@results = {}
|
17
|
+
|
16
18
|
add_timeout(:dead, Timeouts::TIMEOUT) { drop_dead! }
|
17
19
|
end
|
18
20
|
|
21
|
+
def ===(params)
|
22
|
+
return docked? if params == :docked
|
23
|
+
return params == id if String === params
|
24
|
+
return false if @parent
|
25
|
+
return false unless @user_agent
|
26
|
+
|
27
|
+
params.all? do |name, value|
|
28
|
+
property = __send__(name).to_s
|
29
|
+
case value
|
30
|
+
when Regexp then property =~ value
|
31
|
+
when String then property == value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
19
36
|
def ask(command, retries = RETRY_LIMIT)
|
20
37
|
id = tell(command)
|
21
38
|
result_hash = wait_with_timeout(:result) { result(id) }
|
@@ -29,17 +46,13 @@ module Terminus
|
|
29
46
|
ask([:body])
|
30
47
|
end
|
31
48
|
|
32
|
-
def source
|
33
|
-
ask([:source])
|
34
|
-
end
|
35
|
-
|
36
49
|
def current_path
|
37
50
|
return nil unless url = @attributes['url']
|
38
51
|
URI.parse(url).path
|
39
52
|
end
|
40
53
|
|
41
54
|
def current_url
|
42
|
-
@attributes['url']
|
55
|
+
@attributes['url'] || ''
|
43
56
|
end
|
44
57
|
|
45
58
|
def docked?
|
@@ -55,32 +68,67 @@ module Terminus
|
|
55
68
|
nil
|
56
69
|
end
|
57
70
|
|
58
|
-
def find(xpath)
|
59
|
-
ask([:find, xpath, false]).map { |id| Node.new(self, id) }
|
71
|
+
def find(xpath, driver = nil)
|
72
|
+
ask([:find, xpath, false]).map { |id| Node.new(self, id, driver) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def frame!(frame_browser)
|
76
|
+
@frames.add(frame_browser)
|
77
|
+
end
|
78
|
+
|
79
|
+
def frames
|
80
|
+
@frames.to_a
|
81
|
+
end
|
82
|
+
|
83
|
+
def frame_src(name)
|
84
|
+
ask([:frame_src, name])
|
60
85
|
end
|
61
86
|
|
62
87
|
def id
|
63
88
|
@attributes['id']
|
64
89
|
end
|
65
|
-
alias :name :id
|
66
90
|
|
67
91
|
def page_id
|
68
92
|
@attributes['page']
|
69
93
|
end
|
70
94
|
|
71
95
|
def ping!(message)
|
96
|
+
p message if Terminus.debug
|
72
97
|
remove_timeout(:dead)
|
73
98
|
add_timeout(:dead, Timeouts::TIMEOUT) { drop_dead! }
|
99
|
+
|
100
|
+
uri = @controller.rewrite_local(message['url'], @dock_host)
|
101
|
+
message['url'] = uri.to_s
|
102
|
+
|
74
103
|
@attributes = @attributes.merge(message)
|
104
|
+
@user_agent = UserAgent.parse(message['ua'])
|
75
105
|
detect_dock_host
|
106
|
+
|
107
|
+
@infinite_redirect = message['infinite']
|
108
|
+
|
109
|
+
if parent = message['parent']
|
110
|
+
@parent = Terminus.browser(parent)
|
111
|
+
@parent.frame!(self) unless @parent == self
|
112
|
+
end
|
113
|
+
|
76
114
|
@ping = true
|
77
115
|
end
|
78
116
|
|
79
117
|
def reset!
|
80
|
-
|
118
|
+
if url = @attributes['url']
|
119
|
+
uri = URI.parse(url)
|
120
|
+
visit("http://#{uri.host}:#{uri.port}")
|
121
|
+
end
|
122
|
+
ask([:clear_cookies])
|
123
|
+
@attributes.delete('url')
|
124
|
+
end
|
125
|
+
|
126
|
+
def response_headers
|
127
|
+
evaluate_script('TERMINUS_HEADERS')
|
81
128
|
end
|
82
129
|
|
83
130
|
def result!(message)
|
131
|
+
p message if Terminus.debug
|
84
132
|
@results[message['commandId']] = message['result']
|
85
133
|
end
|
86
134
|
|
@@ -90,19 +138,36 @@ module Terminus
|
|
90
138
|
end
|
91
139
|
|
92
140
|
def return_to_dock
|
93
|
-
visit "http://#{@
|
141
|
+
visit "http://#{@dock_host}:#{Terminus.port}/"
|
142
|
+
end
|
143
|
+
|
144
|
+
def source
|
145
|
+
evaluate_script('TERMINUS_SOURCE')
|
146
|
+
end
|
147
|
+
|
148
|
+
def status_code
|
149
|
+
evaluate_script('TERMINUS_STATUS')
|
94
150
|
end
|
95
151
|
|
96
152
|
def tell(command)
|
97
153
|
id = @namespace.generate
|
154
|
+
p [id, command] if Terminus.debug
|
98
155
|
messenger.publish(channel, 'command' => command, 'commandId' => id)
|
99
156
|
id
|
100
157
|
end
|
101
158
|
|
102
159
|
def visit(url, retries = RETRY_LIMIT)
|
103
|
-
|
104
|
-
|
160
|
+
close_frames!
|
161
|
+
uri = @controller.rewrite_remote(url, @dock_host)
|
162
|
+
uri.host = @dock_host if uri.host =~ LOCALHOST
|
163
|
+
tell([:visit, uri.to_s])
|
105
164
|
wait_for_ping
|
165
|
+
|
166
|
+
if @infinite_redirect
|
167
|
+
@infinite_redirect = nil
|
168
|
+
raise Capybara::InfiniteRedirectError
|
169
|
+
end
|
170
|
+
|
106
171
|
rescue Timeouts::TimeoutError => e
|
107
172
|
raise e if retries.zero?
|
108
173
|
visit(url, retries - 1)
|
@@ -113,22 +178,39 @@ module Terminus
|
|
113
178
|
wait_with_timeout(:ping) { @ping or @dead }
|
114
179
|
end
|
115
180
|
|
181
|
+
def to_s
|
182
|
+
"<#{self.class.name} #{name} #{version} (#{os})>"
|
183
|
+
end
|
184
|
+
alias :inspect :to_s
|
185
|
+
|
186
|
+
protected
|
187
|
+
|
188
|
+
def drop_dead!
|
189
|
+
remove_timeout(:dead)
|
190
|
+
close_frames!
|
191
|
+
@dead = true
|
192
|
+
@controller.drop_browser(self)
|
193
|
+
end
|
194
|
+
|
116
195
|
private
|
117
196
|
|
118
197
|
def channel
|
119
198
|
"/terminus/clients/#{id}"
|
120
199
|
end
|
121
200
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
@docked = true
|
126
|
-
@controller.dock_host = uri.host
|
201
|
+
def close_frames!
|
202
|
+
@frames.each { |frame| frame.drop_dead! }
|
203
|
+
@frames = Set.new
|
127
204
|
end
|
128
205
|
|
129
|
-
def
|
130
|
-
|
131
|
-
|
206
|
+
def detect_dock_host
|
207
|
+
uri = URI.parse(@attributes['url'])
|
208
|
+
if uri.port == Terminus.port
|
209
|
+
@docked = true
|
210
|
+
@dock_host = uri.host
|
211
|
+
else
|
212
|
+
@docked = false
|
213
|
+
end
|
132
214
|
end
|
133
215
|
|
134
216
|
def messenger
|