terminus 0.4.0 → 0.5.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/bin/terminus +5 -5
- data/lib/capybara/driver/terminus.rb +24 -13
- data/lib/terminus.rb +21 -15
- data/lib/terminus/application.rb +6 -6
- data/lib/terminus/browser.rb +77 -60
- data/lib/terminus/client.rb +33 -16
- data/lib/terminus/client/browser.rb +10 -9
- data/lib/terminus/client/phantom.js +25 -3
- data/lib/terminus/client/phantomjs.rb +44 -7
- data/lib/terminus/connector.rb +2 -2
- data/lib/terminus/connector/server.rb +15 -15
- data/lib/terminus/connector/socket_handler.rb +11 -11
- data/lib/terminus/controller.rb +62 -26
- data/lib/terminus/headers.rb +25 -0
- data/lib/terminus/host.rb +6 -6
- data/lib/terminus/node.rb +36 -22
- data/lib/terminus/proxy.rb +81 -45
- data/lib/terminus/proxy/driver_body.rb +14 -15
- data/lib/terminus/proxy/external.rb +12 -6
- data/lib/terminus/proxy/rewrite.rb +7 -6
- data/lib/terminus/public/compiled/terminus-min.js +3 -3
- data/lib/terminus/public/compiled/terminus.js +225 -180
- data/lib/terminus/public/pathology.js +87 -87
- data/lib/terminus/public/terminus.js +138 -93
- data/lib/terminus/server.rb +7 -7
- data/lib/terminus/timeouts.rb +8 -8
- data/lib/terminus/views/bootstrap.erb +7 -7
- data/lib/terminus/views/index.erb +4 -4
- data/lib/terminus/views/infinite.html +4 -4
- data/spec/1.1/reports/android.txt +874 -0
- data/spec/{reports → 1.1/reports}/chrome.txt +72 -69
- data/spec/{reports → 1.1/reports}/firefox.txt +72 -69
- data/spec/{reports → 1.1/reports}/opera.txt +72 -69
- data/spec/{reports → 1.1/reports}/phantomjs.txt +72 -69
- data/spec/{reports → 1.1/reports}/safari.txt +72 -69
- data/spec/{spec_helper.rb → 1.1/spec_helper.rb} +5 -2
- data/spec/{terminus_driver_spec.rb → 1.1/terminus_driver_spec.rb} +2 -2
- data/spec/{terminus_session_spec.rb → 1.1/terminus_session_spec.rb} +2 -2
- data/spec/2.0/reports/android.txt +815 -0
- data/spec/2.0/reports/chrome.txt +806 -0
- data/spec/2.0/reports/firefox.txt +812 -0
- data/spec/2.0/reports/opera.txt +806 -0
- data/spec/2.0/reports/phantomjs.txt +803 -0
- data/spec/2.0/reports/safari.txt +806 -0
- data/spec/2.0/spec_helper.rb +21 -0
- data/spec/2.0/terminus_spec.rb +25 -0
- metadata +41 -32
- data/spec/reports/android.txt +0 -875
data/lib/terminus/client.rb
CHANGED
@@ -1,35 +1,52 @@
|
|
1
1
|
module Terminus
|
2
2
|
module Client
|
3
|
-
|
3
|
+
|
4
4
|
autoload :Browser, ROOT + '/terminus/client/browser'
|
5
5
|
autoload :PhantomJS, ROOT + '/terminus/client/phantomjs'
|
6
|
-
|
6
|
+
|
7
7
|
class Base
|
8
|
+
attr_reader :id
|
9
|
+
|
8
10
|
def self.start(options = {})
|
9
|
-
process = new(options)
|
10
|
-
process.start
|
11
|
-
process
|
11
|
+
@process = new(options)
|
12
|
+
@process.start
|
13
|
+
@process
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.debugger
|
17
|
+
@process.debugger
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.save_screenshot(path, options = {})
|
21
|
+
@process.save_screenshot(path, options)
|
12
22
|
end
|
13
|
-
|
23
|
+
|
14
24
|
def initialize(options)
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
25
|
+
@id = Faye.random
|
26
|
+
@options = options
|
27
|
+
@address = TCPServer.new(0).addr
|
28
|
+
@connector = Connector::Server.new(self)
|
29
|
+
@port = options[:port] || @address[1]
|
30
|
+
@terminus = Terminus.create(:port => @port)
|
31
|
+
@browser = ChildProcess.build(*browser_args)
|
19
32
|
end
|
20
|
-
|
33
|
+
|
34
|
+
def debug(*args)
|
35
|
+
p args if Terminus.debug
|
36
|
+
end
|
37
|
+
|
21
38
|
def start
|
22
39
|
Terminus.ensure_reactor_running
|
23
40
|
@terminus.run!
|
24
|
-
|
41
|
+
|
25
42
|
@browser.start
|
26
|
-
|
43
|
+
|
27
44
|
Terminus.port = @port
|
28
45
|
Terminus.browser = browser_selector
|
29
|
-
|
46
|
+
|
30
47
|
at_exit { stop }
|
31
48
|
end
|
32
|
-
|
49
|
+
|
33
50
|
def stop
|
34
51
|
@terminus.stop!
|
35
52
|
@browser.stop
|
@@ -37,7 +54,7 @@ module Terminus
|
|
37
54
|
rescue ChildProcess::TimeoutError
|
38
55
|
end
|
39
56
|
end
|
40
|
-
|
57
|
+
|
41
58
|
end
|
42
59
|
end
|
43
60
|
|
@@ -1,30 +1,31 @@
|
|
1
1
|
module Terminus
|
2
2
|
module Client
|
3
|
-
|
3
|
+
|
4
4
|
class Browser < Base
|
5
5
|
DEFAULT_COMMANDS = {
|
6
6
|
/(mingw|mswin|windows|cygwin)/i => ['cmd', '/C', 'start', '/b'],
|
7
7
|
/(darwin|mac os)/i => ['open'],
|
8
8
|
/(linux|bsd|aix|solaris)/i => ['xdg-open']
|
9
9
|
}
|
10
|
-
|
11
|
-
def browser_args
|
10
|
+
|
11
|
+
def browser_args
|
12
|
+
command = @options[:command]
|
12
13
|
return command + [dock_url] if command
|
13
|
-
|
14
|
+
|
14
15
|
os = RbConfig::CONFIG['host_os']
|
15
16
|
key = DEFAULT_COMMANDS.keys.find { |key| os =~ key }
|
16
17
|
DEFAULT_COMMANDS[key] + [dock_url]
|
17
18
|
end
|
18
|
-
|
19
|
+
|
19
20
|
def browser_selector
|
20
|
-
{:
|
21
|
+
{:raw_url => dock_url}
|
21
22
|
end
|
22
|
-
|
23
|
+
|
23
24
|
def dock_url
|
24
|
-
"http://#{@address[2]}:#{@port}"
|
25
|
+
"http://#{@address[2]}:#{@port}/"
|
25
26
|
end
|
26
27
|
end
|
27
|
-
|
28
|
+
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
@@ -1,6 +1,28 @@
|
|
1
|
-
var page
|
2
|
-
|
3
|
-
|
1
|
+
var page = new WebPage(),
|
2
|
+
width = phantom.args[0],
|
3
|
+
height = phantom.args[1],
|
4
|
+
host = phantom.args[2],
|
5
|
+
port = phantom.args[3],
|
6
|
+
socket = phantom.args[4];
|
4
7
|
|
8
|
+
var Commands = {
|
9
|
+
save_screenshot: function(path, options, callback) {
|
10
|
+
page.render(path);
|
11
|
+
callback(true);
|
12
|
+
}
|
13
|
+
};
|
14
|
+
|
15
|
+
var ws = new WebSocket('ws://' + host + ':' + socket + '/');
|
16
|
+
ws.onmessage = function(message) {
|
17
|
+
var args = JSON.parse(message.data),
|
18
|
+
method = args.shift();
|
19
|
+
|
20
|
+
args.push(function(result) {
|
21
|
+
ws.send(JSON.stringify([result]));
|
22
|
+
});
|
23
|
+
Commands[method].apply(Commands, args);
|
24
|
+
};
|
25
|
+
|
26
|
+
page.viewportSize = {width: parseInt(width, 10), height: parseInt(height, 10)};
|
5
27
|
page.open('http://' + host + ':' + port + '/');
|
6
28
|
|
@@ -1,20 +1,57 @@
|
|
1
1
|
module Terminus
|
2
2
|
module Client
|
3
|
-
|
3
|
+
|
4
4
|
class PhantomJS < Base
|
5
|
+
BROWSERS = %w[chromium chromium-browser google-chrome xdg-open open]
|
6
|
+
DEBUG_PATH = '/webkit/inspector/inspector.html?page=2'
|
5
7
|
DEFAULT_COMMAND = ['/usr/bin/env', 'phantomjs']
|
8
|
+
DEFAULT_WIDTH = 1024
|
9
|
+
DEFAULT_HEIGHT = 768
|
6
10
|
PHANTOM_CLIENT = File.expand_path('../phantom.js', __FILE__)
|
7
|
-
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
@debug_port = TCPServer.new(0).addr[1]
|
14
|
+
@debugger = BROWSERS.map(&method(:detect_browser)).compact.first
|
15
|
+
super
|
11
16
|
end
|
12
|
-
|
17
|
+
|
18
|
+
def browser_args
|
19
|
+
command = @options[:command] || DEFAULT_COMMAND
|
20
|
+
width = @options[:width] || DEFAULT_WIDTH
|
21
|
+
height = @options[:height] || DEFAULT_HEIGHT
|
22
|
+
|
23
|
+
args = command.dup
|
24
|
+
args + ["--remote-debugger-port=#{@debug_port}", "--remote-debugger-autorun=yes",
|
25
|
+
PHANTOM_CLIENT, width.to_s, height.to_s,
|
26
|
+
@address[2], @port.to_s, @connector.port.to_s]
|
27
|
+
end
|
28
|
+
|
13
29
|
def browser_selector
|
14
30
|
{:name => 'PhantomJS'}
|
15
31
|
end
|
32
|
+
|
33
|
+
def debugger
|
34
|
+
raise ArgumentError, 'Could not find launchable browser' unless @debugger
|
35
|
+
url = "http://#{@address[2]}:#{@debug_port}#{DEBUG_PATH}"
|
36
|
+
process = ChildProcess.build(@debugger, url)
|
37
|
+
process.start
|
38
|
+
puts "Launched WebKit remote debugger at #{url}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def detect_browser(browser)
|
42
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
43
|
+
exe = File.join(path, browser)
|
44
|
+
return exe if File.executable?(exe)
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def save_screenshot(path, options = {})
|
50
|
+
message = Yajl::Encoder.encode(['save_screenshot', path, options])
|
51
|
+
@connector.request(message)
|
52
|
+
end
|
16
53
|
end
|
17
|
-
|
54
|
+
|
18
55
|
end
|
19
56
|
end
|
20
57
|
|
data/lib/terminus/connector.rb
CHANGED
@@ -23,11 +23,11 @@
|
|
23
23
|
|
24
24
|
module Terminus
|
25
25
|
module Connector
|
26
|
-
|
26
|
+
|
27
27
|
class Server
|
28
28
|
RECV_SIZE = 1024
|
29
29
|
BIND_TIMEOUT = 5
|
30
|
-
|
30
|
+
|
31
31
|
def initialize(browser, timeout = BIND_TIMEOUT)
|
32
32
|
@browser = browser
|
33
33
|
@skips = 0
|
@@ -35,7 +35,7 @@ module Terminus
|
|
35
35
|
@timeout = timeout
|
36
36
|
reset
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def reset
|
40
40
|
@closing = false
|
41
41
|
@env = nil
|
@@ -43,15 +43,15 @@ module Terminus
|
|
43
43
|
@parser = Http::Parser.new
|
44
44
|
@socket = nil
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def connected?
|
48
48
|
not @socket.nil?
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
def port
|
52
52
|
@server.addr[1]
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
def request(message)
|
56
56
|
@browser.debug(:send, @browser.id, message)
|
57
57
|
accept unless connected?
|
@@ -65,13 +65,13 @@ module Terminus
|
|
65
65
|
reset
|
66
66
|
nil
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def drain_socket
|
70
70
|
@closing = true if @socket
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
private
|
74
|
-
|
74
|
+
|
75
75
|
def start_server
|
76
76
|
time = Time.now
|
77
77
|
TCPServer.open(0)
|
@@ -83,7 +83,7 @@ module Terminus
|
|
83
83
|
raise
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
def accept
|
88
88
|
@skips.times { @server.accept.close }
|
89
89
|
@socket = @server.accept
|
@@ -100,7 +100,7 @@ module Terminus
|
|
100
100
|
@browser.debug(:accept, @browser.id, @handler.url)
|
101
101
|
end
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
def env
|
105
105
|
@env ||= begin
|
106
106
|
env = {'REQUEST_METHOD' => @parser.http_method}
|
@@ -113,11 +113,11 @@ module Terminus
|
|
113
113
|
env
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
def receive
|
118
118
|
@browser.debug(:receive, @browser.id)
|
119
119
|
start = Time.now
|
120
|
-
|
120
|
+
|
121
121
|
until @handler.message?
|
122
122
|
raise Errno::EWOULDBLOCK if (Time.now - start) >= @timeout
|
123
123
|
IO.select([@socket], [], [], @timeout) or raise Errno::EWOULDBLOCK
|
@@ -128,7 +128,7 @@ module Terminus
|
|
128
128
|
end
|
129
129
|
@handler && @handler.next_message
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
def close
|
133
133
|
[server, socket].compact.each do |s|
|
134
134
|
s.close_read
|
@@ -136,7 +136,7 @@ module Terminus
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
@@ -23,50 +23,50 @@
|
|
23
23
|
|
24
24
|
module Terminus
|
25
25
|
module Connector
|
26
|
-
|
26
|
+
|
27
27
|
class SocketHandler
|
28
28
|
attr_reader :env
|
29
|
-
|
29
|
+
|
30
30
|
def initialize(server, env)
|
31
31
|
@server = server
|
32
32
|
@env = env
|
33
33
|
@parser = Faye::WebSocket.parser(env).new(self)
|
34
34
|
@messages = []
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def url
|
38
38
|
"ws://#{env['HTTP_HOST']}/"
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def handshake_response
|
42
42
|
@parser.handshake_response
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def <<(data)
|
46
46
|
@parser.parse(data)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def encode(message)
|
50
50
|
@parser.frame(Faye::WebSocket.encode(message))
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def receive(message)
|
54
54
|
@messages << message
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
def message?
|
58
58
|
@messages.any?
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
def next_message
|
62
62
|
@messages.shift
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
def close(*args)
|
66
66
|
@server.reset
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
data/lib/terminus/controller.rb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
1
|
module Terminus
|
2
2
|
class Controller
|
3
|
-
|
3
|
+
|
4
4
|
include Timeouts
|
5
|
-
|
5
|
+
|
6
6
|
def initialize
|
7
7
|
@connected = false
|
8
8
|
@browsers = {}
|
9
9
|
@host_aliases = {}
|
10
|
+
@local_ports = []
|
11
|
+
@visited = Set.new
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def browser(id = nil)
|
13
15
|
return @browser if id.nil?
|
14
16
|
@browsers[id] ||= Browser.new(self, id)
|
15
17
|
end
|
16
|
-
|
18
|
+
|
17
19
|
def browsers
|
18
20
|
@browsers.values
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def browser=(params)
|
22
24
|
ensure_connection
|
23
25
|
return @browser = params if Browser === params
|
@@ -25,41 +27,46 @@ module Terminus
|
|
25
27
|
@browsers.values.find { |b| b === params }
|
26
28
|
end
|
27
29
|
end
|
28
|
-
|
30
|
+
|
29
31
|
def cookies
|
30
32
|
@cookies ||= CookieJar::Jar.new
|
31
33
|
end
|
32
|
-
|
34
|
+
|
33
35
|
def drop_browser(browser)
|
34
36
|
@browsers.delete(browser.id)
|
35
37
|
@browser = nil if @browser == browser
|
36
38
|
end
|
37
|
-
|
39
|
+
|
38
40
|
def ensure_browsers(n = 1)
|
39
41
|
ensure_connection
|
40
42
|
wait_with_timeout(:browsers) { @browsers.size >= n }
|
41
43
|
end
|
42
|
-
|
44
|
+
|
43
45
|
def messenger
|
44
46
|
Terminus.ensure_reactor_running
|
45
47
|
return @messenger if defined?(@messenger)
|
46
|
-
|
48
|
+
|
47
49
|
@messenger = Faye::Client.new(Terminus.endpoint)
|
48
|
-
|
50
|
+
|
49
51
|
@messenger.subscribe('/terminus/ping', &method(:accept_ping))
|
50
52
|
@messenger.subscribe('/terminus/results', &method(:accept_result))
|
51
53
|
@messenger
|
52
54
|
end
|
53
|
-
|
55
|
+
|
56
|
+
def register_local_port(port)
|
57
|
+
@local_ports << port
|
58
|
+
@host_aliases[Host.new(URI.parse("http://localhost:#{port}/"))] = Host.new(URI.parse("http://localhost:80/"))
|
59
|
+
end
|
60
|
+
|
54
61
|
def return_to_dock
|
55
62
|
@browsers.each { |id, b| b.return_to_dock }
|
56
63
|
end
|
57
|
-
|
64
|
+
|
58
65
|
def rewrite_local(url, dock_host)
|
59
66
|
uri = URI.parse(url)
|
60
67
|
uri.host = '127.0.0.1' if uri.host == dock_host
|
61
68
|
uri.path = '' if uri.path == '/'
|
62
|
-
|
69
|
+
|
63
70
|
# 1.8.7 does not have Hash#key, and 1.9.2 gives warnings for #index
|
64
71
|
remote_host = @host_aliases.respond_to?(:key) ?
|
65
72
|
@host_aliases.key(Host.new(uri)) :
|
@@ -70,41 +77,66 @@ module Terminus
|
|
70
77
|
uri.host = remote_host.host
|
71
78
|
uri.port = remote_host.port
|
72
79
|
end
|
73
|
-
uri.
|
80
|
+
uri.path = '/' if uri.path == ''
|
74
81
|
uri
|
75
82
|
end
|
76
|
-
|
83
|
+
|
77
84
|
def rewrite_remote(url, dock_host = nil)
|
85
|
+
protocol_relative = (url =~ /^\/\//)
|
86
|
+
url = "http:#{url}" if protocol_relative
|
87
|
+
|
78
88
|
uri = URI.parse(url)
|
79
|
-
return uri unless URI::HTTP === uri
|
89
|
+
return uri unless URI::HTTP === uri
|
90
|
+
|
91
|
+
if (uri.host =~ LOCALHOST or uri.host == dock_host)
|
92
|
+
if uri.port == 80
|
93
|
+
uri.port = @host_aliases.keys.find { |h| h.host =~ LOCALHOST }.port
|
94
|
+
end
|
95
|
+
|
96
|
+
if local_ports.include?(uri.port)
|
97
|
+
uri.host = dock_host
|
98
|
+
return uri
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
80
102
|
server = boot(uri)
|
81
|
-
uri.scheme = 'http'
|
103
|
+
uri.scheme = protocol_relative ? nil : 'http'
|
82
104
|
uri.host, uri.port = (dock_host || server.host), server.port
|
83
105
|
uri
|
84
106
|
rescue URI::InvalidURIError
|
85
107
|
url
|
86
108
|
end
|
87
|
-
|
109
|
+
|
88
110
|
def server_running?(server)
|
89
111
|
return false unless server.port
|
90
|
-
uri = URI.parse("http://#{server.host}:#{server.port}
|
112
|
+
uri = URI.parse("http://#{server.host}:#{server.port}#{PING_PATH}")
|
91
113
|
Net::HTTP.start(uri.host, uri.port) { |h| h.head(uri.path) }
|
92
114
|
true
|
93
115
|
rescue
|
94
116
|
false
|
95
117
|
end
|
96
|
-
|
118
|
+
|
119
|
+
def visit_url(url)
|
120
|
+
@visited.add(url)
|
121
|
+
end
|
122
|
+
|
123
|
+
def visited?(url)
|
124
|
+
visited = @visited.member?(url)
|
125
|
+
@visited.delete(url)
|
126
|
+
visited
|
127
|
+
end
|
128
|
+
|
97
129
|
private
|
98
|
-
|
130
|
+
|
99
131
|
def accept_ping(message)
|
100
132
|
browser(message['id']).ping!(message)
|
101
133
|
end
|
102
|
-
|
134
|
+
|
103
135
|
def accept_result(message)
|
104
136
|
browser = @browsers[message['id']]
|
105
137
|
browser.result!(message) if browser
|
106
138
|
end
|
107
|
-
|
139
|
+
|
108
140
|
def boot(remote_uri)
|
109
141
|
host = Host.new(remote_uri)
|
110
142
|
@host_aliases[host] ||= begin
|
@@ -114,13 +146,17 @@ module Terminus
|
|
114
146
|
Host.new(server)
|
115
147
|
end
|
116
148
|
end
|
117
|
-
|
149
|
+
|
118
150
|
def ensure_connection
|
119
151
|
return if @connected
|
120
152
|
messenger.connect { @connected = true }
|
121
153
|
wait_with_timeout(:connection) { @connected }
|
122
154
|
end
|
123
|
-
|
155
|
+
|
156
|
+
def local_ports
|
157
|
+
[Terminus.port] + @local_ports + @host_aliases.values.map { |h| h.port }
|
158
|
+
end
|
159
|
+
|
124
160
|
end
|
125
161
|
end
|
126
162
|
|