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.
@@ -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
+
@@ -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
+
@@ -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 + '/js.packages.js';
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
  });
@@ -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>Terminus is a distributed JavaScript console. You can find out
15
- more at <a href="http://github.com/jcoglan/terminus">its GitHub page</a>.
16
- To connect the console to your browser, you&rsquo;ll need this
17
- bookmarklet. Just drag the word &lsquo;Terminus&rsquo; up to your
18
- bookmark toolbar and click it to connect any page to your console.</p>
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 &copy; 2010
23
- <a href="http://jcoglan.com">James Coglan</a>.
24
- Released under the MIT license.</p>
25
+ <p>Copyright &copy; 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>
@@ -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
- version: 0.1.0
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-04-05 00:00:00 +01:00
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
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: thin
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
46
+ type: :runtime
47
+ version_requirements: *id002
35
48
  - !ruby/object:Gem::Dependency
36
49
  name: eventmachine
37
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
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
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 0.3.2
54
- version:
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
- type: :runtime
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
104
+ type: :runtime
105
+ version_requirements: *id006
65
106
  - !ruby/object:Gem::Dependency
66
107
  name: packr
67
- type: :runtime
68
- version_requirement:
69
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
118
+ type: :runtime
119
+ version_requirements: *id007
75
120
  - !ruby/object:Gem::Dependency
76
121
  name: oyster
77
- type: :runtime
78
- version_requirement:
79
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
132
+ type: :runtime
133
+ version_requirements: *id008
85
134
  description:
86
- email: jcoglan@googlemail.com
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/terminus/server.rb
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.5
191
+ rubygems_version: 1.3.7
129
192
  signing_key:
130
193
  specification_version: 3
131
- summary: Control multiple web browsers from the command line
194
+ summary: Capybara driver for cross-browser testing
132
195
  test_files: []
133
196